#!/usr/bin/python
# RPythonic - July 28th, 2011
# By HartsAntler, bhartsho@yahoo.com
# License: BSD
VERSION = '0.4.1pre3'

# py3k compat

import os, sys, ctypes, inspect
import subprocess

# this wont work, DAM!
#os.environ['LD_LIBRARY_PATH'] = '/usr/local/lib'
#sys.path.append( '/usr/local/lib' )
#http://stackoverflow.com/questions/856116/changing-ld-library-path-at-runtime-for-ctypes

import CppHeaderParser
print( 'loaded CppHeaderParser' )

try:
	import pycparser		# including pycparser-2.03
	print( 'loaded pycparser' )


except:
	print( 'MESSAGE: failed to import pycparser - rpythonic can not generate new wrappers' )
	pycparser = None


RPYTHONIC_DIR = os.path.split(os.path.abspath(__file__))[0]
CTYPES_HEADER = '## generated by RPythonic %s\n' % VERSION
CTYPES_HEADER += '## http://code.google.com/p/rpythonic/'
CTYPES_HEADER += '\n' + open( os.path.join(RPYTHONIC_DIR,'magicheader.py'), 'rb' ).read().decode('utf-8')



_doc_ = '''
NAME
	RPythonic

DESCRIPTION
	RPythonic is a frontend for using RPython (the translation toolchain of PyPy), it simplifies: wrapper generation, compiling (standalone or modules), and Android integration.  It can also be used as a tool to automatically wrap C/C++ libraries using ctypes.

INSTALLING
		PLY (apt-get install python-ply)
		python setup.py install

RPYTHON API
	import rpythonic
	rpythonic.set_pypy_root( '../../pypy' )
	rpy = rpythonic.RPython()
	@rpy.bind(a=float, b=float)
	def sub( a, b ): return a-b
	rpy.cache('test1')
	sub(10,100)		# compiled

WRAPPER API
	import rpythonic
	try: module = rpythonic.load( 'OpenNI' )
	except: module = rpythonic.wrap( 'OpenNI', header='/usr/include/ni/XnOpenNI.h', library='/usr/lib/libOpenNI.so' )


'''

# TODO enable inlining
# func._dont_inline_ = True 
# func._always_inline_ = True
# stackless test1 - april25
#flow space unrolls loops
#wrapped = rlib.unrolling_iterable( somelist )
# moving to using secondary_entrypoints using carbonpython's style
#apt-get install libgc-dev
#apt-get install git-core cmake libglut3-dev pkg-config build-essential libxmu-dev libxi-dev libusb-1.0-0-dev


#############################################################
# from pypy/translator/stackless/transform.py
# a simple example of what the stackless transform does
#
# def func(x):
#     return g() + x + 1
#
# STATE_func_0 = lltype.Struct('STATE_func_0',
#                              ('header', STATE_HEADER),
#                              ('saved_long_0', Signed))
#
# def func(x):
#     state = global_state.restart_substate
#     if state == -1:   # normal case
#         try:
#             retval = g(x)
#         except code.UnwindException, u:
#             state = lltype.malloc(STATE_func_0, flavor='gc_nocollect')
#             state.header.f_restart = <index in array of frame.FRAMEINFO>
#             state.saved_long_0 = x
#             code.add_frame_state(u, state.header)
#             raise
#     elif state == 0:
#         global_state.restart_substate = -1
#         state = lltype.cast_pointer(lltype.Ptr(STATE_func_0),
#                                     global_state.top)
#         global_state.top = null_state
#         x = state.saved_long_0
#         retval = code.fetch_retval_long() # can raise an exception
#     elif state == 1:
#         ...
#     elif state == 2:
#         ...
#     else:
#         abort()
#     return retval + x + 1



IS32BIT = (ctypes.sizeof(ctypes.c_void_p)==4)
SEM_NAME = '/rpython_mutex'
PATH2PYPY = '../pypy'


RAYMOND_HETTINGER = '''
################### Raymond Hettinger's Constant Folding ##################
# Decorator for BindingConstants at compile time
# A recipe by Raymond Hettinger, from Python Cookbook:
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/277940
# updated for Python3 and still compatible with Python2 - by Hart, May17th 2011

try: _BUILTINS_DICT_ = vars(__builtins__)
except: _BUILTINS_DICT_ = __builtins__
ISPYTHON2 = sys.version_info[0] == 2
_HETTINGER_FOLDS_ = 0

def _hettinger_make_constants(f, builtin_only=False, stoplist=[], verbose=0):
    from opcode import opmap, HAVE_ARGUMENT, EXTENDED_ARG
    global _HETTINGER_FOLDS_
    try:
        if ISPYTHON2: co = f.func_code; fname = f.func_name
        else: co = f.__code__; fname = f.__name__
    except AttributeError: return f        # Jython doesn't have a func_code attribute.
    if ISPYTHON2: newcode = map(ord, co.co_code)
    else: newcode = list( co.co_code )
    newconsts = list(co.co_consts)
    names = co.co_names
    codelen = len(newcode)
    if ISPYTHON2:
        if verbose >= 2: print( f.func_name )
        func_globals = f.func_globals
    else:
        if verbose >= 2: print( f.__name__ )
        func_globals = f.__globals__

    env = _BUILTINS_DICT_.copy()
    if builtin_only:
        stoplist = dict.fromkeys(stoplist)
        stoplist.update(func_globals)
    else:
        env.update(func_globals)

    # First pass converts global lookups into constants
    i = 0
    while i < codelen:
        opcode = newcode[i]
        if opcode in (EXTENDED_ARG, opmap['STORE_GLOBAL']):
            if verbose >= 1: print('skipping function', fname)
            return f    # for simplicity, only optimize common cases
        if opcode == opmap['LOAD_GLOBAL']:
            oparg = newcode[i+1] + (newcode[i+2] << 8)
            name = co.co_names[oparg]
            if name in env and name not in stoplist:
                value = env[name]
                for pos, v in enumerate(newconsts):
                    if v is value:
                        break
                else:
                    pos = len(newconsts)
                    newconsts.append(value)
                newcode[i] = opmap['LOAD_CONST']
                newcode[i+1] = pos & 0xFF
                newcode[i+2] = pos >> 8
                _HETTINGER_FOLDS_ += 1
                if verbose >= 2:
                    print( "    global constant fold:", name )
        i += 1
        if opcode >= HAVE_ARGUMENT:
            i += 2

    # Second pass folds tuples of constants and constant attribute lookups
    i = 0
    while i < codelen:

        newtuple = []
        while newcode[i] == opmap['LOAD_CONST']:
            oparg = newcode[i+1] + (newcode[i+2] << 8)
            newtuple.append(newconsts[oparg])
            i += 3

        opcode = newcode[i]
        if not newtuple:
            i += 1
            if opcode >= HAVE_ARGUMENT:
                i += 2
            continue

        if opcode == opmap['LOAD_ATTR']:
            obj = newtuple[-1]
            oparg = newcode[i+1] + (newcode[i+2] << 8)
            name = names[oparg]
            try:
                value = getattr(obj, name)
                if verbose >= 2: print( '    folding attribute', name )
            except AttributeError:
                continue
            deletions = 1

        elif opcode == opmap['BUILD_TUPLE']:
            oparg = newcode[i+1] + (newcode[i+2] << 8)
            if oparg != len(newtuple): continue
            deletions = len(newtuple)
            value = tuple(newtuple)

        else: continue

        reljump = deletions * 3
        newcode[i-reljump] = opmap['JUMP_FORWARD']
        newcode[i-reljump+1] = (reljump-3) & 0xFF
        newcode[i-reljump+2] = (reljump-3) >> 8

        n = len(newconsts)
        newconsts.append(value)
        newcode[i] = opmap['LOAD_CONST']
        newcode[i+1] = n & 0xFF
        newcode[i+2] = n >> 8
        i += 3
        _HETTINGER_FOLDS_ += 1
        if verbose >= 2:
            print( "    folded constant:",value )

    if ISPYTHON2:
        codestr = ''.join(map(chr, newcode))
        codeobj = type(co)(co.co_argcount, co.co_nlocals, co.co_stacksize,
                        co.co_flags, codestr, tuple(newconsts), co.co_names,
                        co.co_varnames, co.co_filename, co.co_name,
                        co.co_firstlineno, co.co_lnotab, co.co_freevars,
                        co.co_cellvars)
        return type(f)(codeobj, f.func_globals, f.func_name, f.func_defaults, f.func_closure)
    else:
        codestr = b''
        for s in newcode: codestr += s.to_bytes(1,'little')
        codeobj = type(co)(co.co_argcount, co.co_kwonlyargcount, co.co_nlocals, co.co_stacksize,
                        co.co_flags, codestr, tuple(newconsts), co.co_names,
                        co.co_varnames, co.co_filename, co.co_name,
                        co.co_firstlineno, co.co_lnotab, co.co_freevars,
                        co.co_cellvars)
        return type(f)(codeobj, f.__globals__, f.__name__, f.__defaults__, f.__closure__)


def hettinger_bind_recursive(mc, builtin_only=False, stoplist=[],  verbose=0):
    """Recursively apply constant binding to functions in a module or class.

    Use as the last line of the module (after everything is defined, but
    before test code).  In modules that need modifiable globals, set
    builtin_only to True.

    """
    import types
    try: d = vars(mc)
    except TypeError: return
    if ISPYTHON2: recursivetypes = (type, types.ClassType)
    else: recursivetypes = (type,)
    for k, v in d.items():
        if type(v) is types.FunctionType:
            newv = _hettinger_make_constants(v, builtin_only, stoplist,  verbose)
            setattr(mc, k, newv)
        elif type(v) in recursivetypes:
            hettinger_bind_recursive(v, builtin_only, stoplist, verbose)

def hettinger_transform( module=None ):
    global _HETTINGER_FOLDS_
    _HETTINGER_FOLDS_ = 0
    if not module: module = sys.modules[__name__]
    hettinger_bind_recursive( module, verbose=1 )
    print( 'HETTINGER: constants folded', _HETTINGER_FOLDS_ )
'''
################### END Raymond Hettinger's Constant Folding ##################
exec( RAYMOND_HETTINGER )


def pprint( txt, color=None ):
	if color and sys.platform != "win32": print( '\x1b[%sm'%color + str(txt) + '\x1b[0m' )
	else: print( txt )

CACHEDIR = None
def set_cache( path ):
	global CACHEDIR
	assert os.path.isdir( path )
	CACHEDIR = path
	if CACHEDIR not in sys.path: sys.path.append( CACHEDIR )
#set_cache( os.path.join( RPYTHONIC_DIR, 'cache' ) )
DEFAULT_CACHEDIR = os.path.join(os.environ['HOME'], '.rpythonic')
if not os.path.isdir( DEFAULT_CACHEDIR ): os.mkdir( DEFAULT_CACHEDIR )
set_cache( DEFAULT_CACHEDIR )

def set_pypy_root( path ):
	global PATH2PYPY
	path = os.path.abspath( path )
	assert os.path.isdir( path )
	PATH2PYPY = path
	if path not in sys.path: sys.path.append( PATH2PYPY )


def set_android_sdk_root(path): AndroidPackage.set_sdk_root( path )
def set_android_ndk_root(path): AndroidPackage.set_ndk_root( path )



def _load( name, type, platform='' ):
	return __import__( 
		'gen%s%s.%s'%(type,platform,name), 
		fromlist=[name] 
	)

def load( name, mode='ctypes', platform='', debug=True ):	# load module
	mod = None
	if debug: mod = _load( name, mode, platform )
	else:
		try: mod = _load( name, mode, platform )
		except: print( 'failed to load module', name )
	return mod

class ModuleWrapper(object):
	## backdoor to define manual wrappers
	# module( somefunc, returns, arg )
	def __call__(self, name, result=ctypes.c_void_p, *args ):
		_args = args
		if args and type(args[0]) not in (tuple,list):
			_args = []
			for i, arg in enumerate( args ):
				_args.append( ('unnamed%s'%i, arg) )
		return self.__module._rpythonic_function_( name, result, _args )

	def __init__(self, name, module, secondary=None):
		self.__name = name
		self.__module = module
		self.__secondary = secondary
		ignorelist = getattr( module, 'RPYTHONIC_AUTOPREFIX_IGNORE' )
		count = {}
		for n in dir( module ):
			if len(n)>=3:
				a = ''
				for i,char in enumerate( n ):
					if i != 0:
						if char.isupper(): break	# camelCase or CamelCase or prefix_name
						elif char == '_': a += '_'; break
					a += char
				if len(a) < len(n):
					if a not in count: count[a] = 0
					count[a] += 1
		self.__try_names = []
		for n in count:
			if count[n] > 20 and n not in ignorelist:
				print( 'auto prefix', n )
				self.__try_names.append( n )

	def __getattr__(self, name ):
		mod = self.__module
		if hasattr( mod, name ): return getattr( mod, name )
		elif hasattr( mod, self.__name+name ): return getattr( mod, self.__name+name )
		else:
			for prefix in self.__try_names:
				if hasattr( mod, prefix+name ): return getattr( mod, prefix+name )
			if self.__secondary: return getattr( self.__secondary, name )
			else: raise AttributeError

def module( name, space=None, mode='ctypes', platform='', secondary=None, fold_constants=False ):
	mod = load( name, mode, platform )
	if fold_constants: mod.hettinger_transform()
	if mod:
		if type(space) is dict:
			for n in dir(mod):
				if not n.startswith('__'): space[ n ] = getattr(mod,n)
		return ModuleWrapper( name, mod, secondary )

########### Wrapper API ############

CTYPES_FOOTER = ''
def wrap(
	name, 
	header=None, 
	insert_headers=[], 
	library=None, 
	dynamic_libs=[], 
	static_libs=[],
	defines=[], 
	undefines=[], 
	includes=[], 
	ctypes=True, 
	rffi=False, 
	cplusplus=False, 
	platform='linux', 
	system_include=None, 
	ignore_classes = [],
	ignore_functions = [],
	ctypes_footer='' ):

	global LIBS, CTYPES_OUTPUT, RFFI_OUTPUT, INCLUDE_DIRS, SYS_INCLUDE_DIRS, MACRO_DEFS, MACRO_UNDEFS, INSERT_HEADERS, CTYPES_FOOTER

	_reset_wrapper_state()
	LIBS = []
	INCLUDE_DIRS = list(includes)		# copy since we modify it
	INSERT_HEADERS = list( insert_headers )
	SYS_INCLUDE_DIRS = []
	CTYPES_FOOTER = ctypes_footer

	if system_include:
		SYS_INCLUDE_DIRS.append( system_include )
		INCLUDE_DIRS.append( system_include )
		for n in os.listdir( system_include ):
			if n=='linux':		# for stddef.h
				sub = os.path.join( system_include, n )
				if sub not in INCLUDE_DIRS: INCLUDE_DIRS.append( sub )

	if not library:
		libname = '%s.so' %name
		if not libname.startswith('lib'): libname = 'lib'+libname
		guess1 = os.path.join( '/usr/local/lib', libname )
		guess2 = os.path.join( '/usr/lib', libname )
		if os.path.isfile( guess1 ): library = guess1
		elif os.path.isfile( guess2 ): library = guess2


	if library: LIBS.append( library )
	#for d in defines: MACRO_DEFS.append( d )
	MACRO_DEFS = list( defines )
	MACRO_UNDEFS = list( undefines )

	if ctypes:
		pre = os.path.join( 'genctypes', name )	# ctypes should not have a platform?
		mdir = os.path.join( CACHEDIR, pre )
		if not os.path.isdir( mdir ):
			os.makedirs( mdir )
			open( os.path.join(os.path.join(CACHEDIR,'genctypes'),'__init__.py'), 'wb' )
		CTYPES_OUTPUT = os.path.join(pre,'__init__.py')

	if rffi:
		pre = os.path.join( 'rffi'+platform, name )
		mdir = os.path.join( CACHEDIR, pre )
		if not os.path.isdir( mdir ):
			os.makedirs( mdir )
			open( os.path.join(os.path.join(CACHEDIR,'rffi'+platform),'__init__.py'), 'wb' )
		RFFI_OUTPUT = os.path.join(pre,'__init__.py')

	if cplusplus:
		a = CPlusPlus( header )
		for lib in dynamic_libs: a.add_library( lib )
		for lib in static_libs: a.add_library( lib, dynamic=False )
		for n in ignore_classes: a.add_ignore_class( n )
		for n in ignore_functions: a.add_ignore_function( n )


	else: a = C( header )

	a.save( name )

	if not platform:
		return _load( name, 'ctypes' )



#################################################################
INSERT_HEADERS = []	# for fixing bad headers, or combine multiple headers into one
HEADERS = []		# DEPRECATE TODO
INCLUDE_DIRS = []
MACRO_DEFS = []
MACRO_UNDEFS = []
LIB_NAME = ''	# not used?
LIB_PATH = ''	# not used?
LIB = ''		# ctypes header
FAKE_LIBC = None
LIBS = []
SYS_INCLUDE_DIRS = []
CTYPES_OUTPUT = None
RFFI_OUTPUT = None

CONFIG = {
	'include_dirs'	: INCLUDE_DIRS,
	'link_libs'		: LIBS,
	'extra_headers'	: HEADERS,
}


def translate_rpython( func, inline=True, compile=False, stackless=False, gc='ref', functions=[] ):
	from pypy.translator.interactive import Translation
	if stackless: assert gc != 'ref'	# this can go away later when pypy supports stackless+ref
	# other gc types: 'ref', 'framework', 'framework+asmgcroot', 'hybrid'

	t = Translation( func, standalone=True, inline=inline, gc=gc, stackless=stackless)

	t.driver.secondary_entrypoints = functions
	print( 'secondary entry points', t.driver.secondary_entrypoints )

	print('-'*80); print('#### PYPY FLOWGRAPH STEP1 COMPLETE ####'); print('-'*80)
	t.annotate()
	print('-'*80); print('#### PYPY ANNONTATION STEP2 COMPLETE ####'); print('-'*80)
	#print( 'ann_argtypes', t.ann_argtypes )
	args = [int]	# force int makes standalone compatible, main must return int
	t.rtype( None )	#interactive.py", line 66, in ensure_setup raise Exception("inconsistent argtype supplied")
	print('-'*80); print('#### PYPY RTYPER STEP3 COMPLETE ####'); print('-'*80)

	predeclare = ''
	t.source_c(); print('-'*80); print('#### PYPY SOURCE GENERATION STEP4 COMPLETE ####'); print('-'*80)
	headers = []
	sources = []
	## copy generated source to our jni directory ##
	for f in t.driver.cbuilder.targetdir.listdir():		# after source_c is called this is available
		if f.ext == '.h': headers.append( f )
		elif f.ext == '.c': sources.append( f )
	for f in t.driver.cbuilder.extrafiles:
		if f not in sources: sources.append( f )	# module_cache/module_#.c

	if compile: return t.compile_c()
	else: return headers, sources

# just for testing #####################
def test_rpy(x):
	a = 'helloworld'
	print( a )
	b = ['a','b']
	for i in b: print( i )	# <PtrRepr * Array of Char >
	for j in ['hello', 'world']: print( j )
	c = 1 + 99
	print( c  )
	return 1	#Exception: stand-alone program entry point must return an int (and not, e.g., None or always raise an exception)




def scan_cmake( url ):
	cwd = os.getcwd()
	path,cmake = os.path.split(url)
	data = open(url,'rb').read()
	os.chdir( path )
	do = False
	paths = []
	ifdefs = {}
	prevline = None
	debug = ''
	for line in data.splitlines():
		line = line.strip()
		if not line: continue	# skip whitespace
		debug += line + '\n'
		if line.startswith( 'INCLUDE_DIRECTORIES(' ):
			if do: raise SyntaxError
			do = True
			line = line.replace('INCLUDE_DIRECTORIES(', '')	# expects single per line
		if do:
			if ')' in line: do = False; line = line.replace(')','')
			for chk in line.split():
				if '$' in chk: print( 'warning: can not parse $ syntax ->', chk )
				elif os.path.isdir( chk ):
					p = os.path.abspath( chk )
					if prevline:
						if prevline.startswith('IF('):
							ifdef = prevline.split('IF(')[-1].split(')')[0].strip()
							print( 'looks like a IF def macro ->', ifdef )
							#if ifdef not in ifdefs: ifdefs[ ifdef ] = []
							#if p not in ifdefs[ ifdef ]: ifdefs[ ifdef ].append( p )
							ifdefs[ ifdef ] = p
						elif p not in paths: paths.append( p )
					elif p not in paths: paths.append( p )
				else: print( debug); raise SyntaxError
		prevline = line

	os.chdir(cwd)
	#print paths
	#print ifdefs
	return {'includes': paths, 'ifdefs':ifdefs }


def find_header_directory( name, path='.' ):
	if name in os.listdir( path ): return path
	for p in INCLUDE_DIRS:
		if name in os.listdir( p ): return p

	if '--scan-cmake' in sys.argv and 'CMakeLists.txt' in os.listdir( path ):
		cmake = scan_cmake( os.path.join( path, 'CMakeLists.txt' ) )
		for p in cmake['includes']:
			if name in os.listdir( p ): return p
		for macro in MACRO_DEFS:
			if macro in cmake['ifdefs']:
				p = cmake['ifdefs'][macro]
				if name in os.listdir( p ): return p
		if cmake['ifdefs']:
			for macro in cmake['ifdefs']:
				print( 'warning: using undefined macro name:', macro )
				p = cmake['ifdefs'][macro]
				if name in os.listdir( p ): return p

	if '/' in name:		# bug fix dec5th, sometimes headers have path/name/header.h
		a,b = os.path.split( name )
		if b in os.listdir( path ): return path
		for p in INCLUDE_DIRS:
			if b in os.listdir( p ): return p


	print( 'warning: resorting to final hacks to find headers' )
	for n in os.listdir( path ):	# check subdirs
		a = os.path.join( path, n )
		if os.path.isdir( a ) and not n.startswith('.'):
			if name in os.listdir( a ): return a

	## check one directory up, then subs
	a,b = os.path.split( path )
	if name in os.listdir(a): return a
	for n in os.listdir( a ):
		c = os.path.join( a, n )
		if c != path and os.path.isdir(c) and not n.startswith('.'):
			if name in os.listdir( c ): return c

	print( 'error: failed to find header ->', name )



def isclass( ob, *names ):
	cname = ob.__class__.__name__
	for name in names:
		if cname == name: return True



b7 = bug = b2=None
odebug = None

def _reset_wrapper_state():
	SomeThing.Structs.clear()
	SomeThing.Unions.clear()
	SomeThing.Functions.clear()
	SomeThing.Enums.clear()
	SomeThing.SomeThings.clear()
	SomeThing.Arrays.clear()

	SomeThing.Nodes = []
	SomeThing.MACRO_GLOBALS.clear()
	SomeThing.Types.clear()
	SomeThing.Typedefs.clear()
	SomeThing.EnumTypes.clear()


class SomeThing(object):
	'''
	ID, Constant, Typename, Decl, or any subtype of Decl:
		TypeDecl
		PtrDecl
			[ subtypes ]
	'''
	Structs = {}
	Unions = {}
	Functions = {}
	Enums = {}
	SomeThings = {}
	Arrays = {}

	Nodes = []
	MACRO_GLOBALS = {}	# TODO, is there a better way to deal with this?


	@staticmethod
	def sort( things ):
		for parent in things:
			pidx = things.index( parent )
			for child in parent.fields:
				type = child.type()
				if type.startswith('struct:') or type.startswith('union:'):
					if child.cyclic: continue#; print('skipping cyclic', type); 	# ctypes
					name = type.split(':')[-1]
					#print('searching', name)
					found = False
					for cidx,c in enumerate( things ):
						if c._name() == name:
							found = True
							if cidx > pidx:
								print('--sort--', name, cidx, pidx)
								things.remove( c )
								things.insert( pidx, c )
								return True
					if not found: print('not found', name)

	@staticmethod
	def get_unions_and_structs(sort=False):
		valid = SomeThing.Unions.values() + SomeThing.Structs.values()
		r = []
		for node in SomeThing.Nodes:
			if node in valid: r.append( node )
		if not sort: return r
		else:
			i = 0
			while sort and i < 400:
				sort = SomeThing.sort( r )
				i += 1
			print( 'sorts', i )
			return r


	@staticmethod
	def get_enums():
		r = []; a = SomeThing.Enums.values(); a.sort()
		for e in a:
			if e not in r: r.append( e )
		return r

	@staticmethod
	def get_unions(): return SomeThing.Unions.values(); a.sort()
	@staticmethod
	def get_structs(): return SomeThing.Structs.values(); a.sort()

	@staticmethod
	def depsort( a ):
		sorts = 0
		for x in a:
			if x.dependson and x.dependson in a:
				if a.index( x ) > a.index( x.dependson ):
					sorts += 1
					a.remove( x )
					print( 'depsorting', x )
					a.insert( a.index(x.dependson)-1, x )
		return sorts

	@staticmethod
	def get_funcs():
		r = []; a = SomeThing.Functions.values(); a.sort()
		for e in a:
			if e not in r:
				if e.name() not in SomeThing.Structs:	# may16th 2011
					r.append( e )
		return r

	def get_ancestors(self, ancest=None):
		if ancest is None: ancest = []
		if self.parent:
			ancest.append( self.parent )
			return self.parent.get_ancestors( ancest )
		else: return ancest

	PYTHON_RESERVED_KEYWORDS = 'for while in as global with try except lambda return raise if else elif eval exec and not or break continue finally print yield del def class assert'.split()

	ReservedNames = 'id object type enumerate isinstance len globals locals tuple dict list int float str bool getattr setattr hasattr range None self'.split() + PYTHON_RESERVED_KEYWORDS
	def name(self):	# note: Decl can have a .name but it can be None, decl.type.name should then have the real name
		if hasattr(self.ast, 'name' ) and self.ast.name:
			if self.ast.name in SomeThing.ReservedNames: return 'C_%s' %self.ast.name
			else: return self.ast.name
		elif hasattr(self.ast, 'declname') and self.ast.declname:
			if self.ast.declname in SomeThing.ReservedNames: return 'C_%s' %self.ast.declname
			else: return self.ast.declname
		elif self.child: return self.child.name()

	def gen_rffi(self, declare=False):
		if self.child and self.child.name(): return self.child.gen_rffi( declare )
		elif self.tag == 'TypeDecl': return '#%s							<TypeDecl=%s>' %(self.name(),self.type())
		else: return '#%s							<ast=%s>' %(self.name(),self.tag)
	def gen_ctypes(self, declare=False):
		if self.child and self.child.name(): return self.child.gen_ctypes( declare )
		elif self.tag == 'TypeDecl': return '#%s							<TypeDecl=%s>' %(self.name(),self.type())
		else: return '#%s							<ast=%s>' %(self.name(),self.tag)

	def gen_python(self):
		if self.child and self.child.name(): return self.child.gen_python()
		else: return ['#%s							<ast=%s>' %(self.name(),self.tag) ]

	@classmethod
	def get_node_by_name( self, name ):
		for n in self.Nodes:
			if n.name() == name: return n

	Types = {}
	Typedefs = {}
	EnumTypes = {}	# typedef'ed enums are different?
	def setup( self ): pass		# overloaded

	def __init__(self, ast, parent=None ):
		SomeThing.Nodes.append( self )
		self.cyclic = False		# for structs and unions
		self.ast = ast
		self.tag = ast.__class__.__name__
		self.parent = parent
		self.array = None		# pass the array down the tree
		if parent:
			## TODO, is this required?
			if isclass( parent, 'Array' ): self.array = parent
			elif parent.array: self.array = parent.array
			elif parent.parent and isclass( parent.parent, 'Array' ):
				self.array = parent.parent
			elif parent.parent and parent.parent.array:
				self.array = parent.parent.array

		self.pointer = isclass( ast, 'PtrDecl' )
		self.child = None
		child = None
		if hasattr( ast, 'type' ): child = ast.type

		if child:
			if isclass( child, 'Struct' ):		self.child = Struct( child, parent=self )
			elif isclass( child, 'Union' ):		self.child = Union( child, parent=self )
			elif isclass( child, 'FuncDef', 'FuncDecl' ): self.child = Function( child, parent=self )
			elif isclass( child, 'Enum' ): self.child = Enum( child, parent=self )
			elif isclass( child, 'ArrayDecl'):	#, 'ArrayRef' ):
				child.show()
				self.child = Array( child, parent=self )
				if self.tag == 'Typedef': self.array = self.child	#TODO revisit
				elif self.tag == 'Decl': self.array = self.child	# structs that contain arrays - may5th

			else:	self.child = SomeThing( child, parent=self )

		if self.tag == 'Typedef':
			#print( 'Typedef: %s' %ast.name )
			#ast.show()
			if ast.name in SomeThing.Typedefs:
				#print( SomeThing.Typedefs.keys())
				print( '---------- warning duplicate typedef ----------' )
				print( ast.name )
				ast.show()
				#raise SyntaxError
			SomeThing.Typedefs[ ast.name ] = self

		if self.tag == 'TypeDecl':
			if isclass( ast.type, 'IdentifierType' ):
				if not ast.type.names: ast.show()
				else: SomeThing.Types[ ast.declname ] = ' '.join( ast.type.names )	# fixed may5th
			elif isclass( ast.type, 'Struct', 'Union' ):
				SomeThing.Types[ ast.declname ] = self.child
			elif isclass( ast.type, 'Enum' ):
				SomeThing.EnumTypes[ ast.declname ] = self.child
			else:
				print('TODO - non ident types'); ast.show()

		ancest = self.get_ancestors()
		self.dependson = None
		for p in ancest:
			if isclass( p, 'Struct', 'Union' ):
				self.dependson = p
				break

		self.setup()
		self.unnamed = False
		name = self.name()
		if not self.parent and name is None:
			self.unnamed = True
			if self.child and self.child.tag != 'Enum':
				raise

		if not name and self.parent:		# fixes nested struct problem
			name = self.parent.name()
		if name:
			a = getattr( SomeThing, self.__class__.__name__+'s' )
			if name in a:
				other = a[name]
				if self.ast.__class__.__name__.endswith('Decl'): pass
				elif other.ast.__class__.__name__.endswith('Decl'): a[name] = self
				else: a[ name ] = self.resolve_conflict( other )
			else: a[ name ] = self



	def num_ast_nodes( self ): return len(self.flatten_ast_nodes( self.ast ) )
	def flatten_ast_nodes( self, ast, nodes=None ):
		if nodes is None: nodes = []
		nodes.append( ast )
		children = ast.children()
		if children: [self.flatten_ast_nodes( child, nodes ) for child in children ]
		return nodes
		
	def resolve_conflict( self, other ):		# the one with more ast nodes is the one we want rule, never breaks?
		if self.num_ast_nodes() > other.num_ast_nodes(): return self
		else: return other

	def pointers( self, p=None ):		# p=[]		bad idea
		if p is None: p = []		# prevents memleak
		if self.pointer: p.append( self )
		if self.child: return self.child.pointers( p )
		else: return p

	def get_child_by_type( self, t ):	# june1st 2011
		if isinstance( self, t ): return self
		elif self.child: return self.child.get_child_by_type( t )

	RFFI_TYPEDEF_HACKS = {
		'int8_t'	:	'SIGNEDCHAR',
		'uint8_t'	:	'UCHAR',
		'int16_t'	:	'SHORT',
		'uint16_t'	:	'USHORT',
		'int32_t'	:	'INT',
		'uint32_t'	:	'UINT',
		'int64_t'	:	'LONG',
		'uint64_t'	:	'ULONG',
		'__builtin_va_list' :	'VOIDP',
	}

	def rffi_type( self ):
		type = self.type()
		typedef = self.typedef()
		pointers = len(self.pointers())
		array = 0
		if self.array: array += self.array.length()
		if typedef:
			pointers += len( typedef.pointers() )
			if typedef.array: array += typedef.array.length()

		ctype = None

		if ':' in type:
			if type.startswith('union:') or type.startswith('struct:'):
				if not self.name(): ctype = 'rffi.VOIDP'	# opaque, un-named union or struct
				else: a,ctype = type.split(':')
			elif type.startswith('enum:'): ctype = 'rffi.INT'
			elif type.startswith('function:'):
				if typedef and typedef.name() in SomeThing.Functions:
					ctype = SomeThing.Functions[typedef.name()].gen_rffi_funcdef()
				else:
					#self.ast.show()
					fdef = self.get_child_by_type( Function )
					#if not fdef and isinstance( self.parent, Function ): fdef = self.parent	# some bug, TODO fix me
					if fdef: ctype = fdef.gen_rffi_funcdef()
					else: ctype = 'rffi.VOIDP'
				#else: raise NotImplemented
				#else: ctype = 'rffi.VOIDP'

		else:
			_type = type.split()
			if 'unsigned' in _type:
				unsigned = 'U'
				_type.remove( 'unsigned' )
			else: unsigned = ''
			if 'signed' in _type:
				assert not unsigned
				_type.remove( 'signed' )

			if _type.count( 'long' ) == 2: ctype = 'rffi.%sLONGLONG' %unsigned
			elif 'double' in _type and 'long' in _type: ctype = 'rffi.LONGDOUBLE'
			elif 'int' in _type and 'short' in _type: ctype = 'rffi.%sSHORT' %unsigned
			elif 'int' in _type and 'long' in _type: ctype = 'rffi.%sLONG' %unsigned
			else:
				if len(_type) != 1:
					self.ast.show()
					print( 'WARN: this should never happen', _type )
					_type = ['void']
				type = _type[0]
				if type == 'void': ctype = 'rffi.VOIDP'
				elif type == 'size_t': ctype = 'rffi.SIZE_T'
				elif type == 'ssize_t': ctype = 'rffi.SSIZE_T'
				elif type[-1].isdigit():
					if type == 'int8':		ctype = 'rffi.SIGNEDCHAR'
					elif type == 'uint8':	ctype = 'rffi.UCHAR'
					elif type == 'int16':		ctype = 'rffi.SHORT'
					elif type == 'uint16':	ctype = 'rffi.USHORT'
					elif type == 'int32':		ctype = 'rffi.INT'
					elif type == 'uint32':		ctype = 'rffi.UINT'
					elif type == 'int64':		ctype = 'rffi.LONG'
					elif type == 'uint64':		ctype = 'rffi.ULONG'
					else:
						print( type )
						raise NotImplemented
				elif type in self.RFFI_TYPEDEF_HACKS:
					ctype = 'rffi.%s' %self.RFFI_TYPEDEF_HACKS[ type ]
				else:
					if type.endswith('_t'):
						self.ast.show(); print( type ); assert 0
					ctype = 'rffi.%s%s'%(unsigned,type.upper())


		if not ctype:
			self.ast.show()
			if self.type() == '<unknown-type>':
				ctype = 'rffi.VOIDP'; print('TODO FIXME')
			else:
				print( self.type() )
				raise NotImplemented

		## special case void
		if type == 'void' or ctype == 'rffi.VOIDP':
			if pointers == 1: ctype = 'rffi.VOIDP'
			elif pointers == 2: ctype = 'rffi.VOIDPP'
			elif pointers == 3: ctype = 'rffi.CArrayPtr( rffi.VOIDPP )'
			elif not pointers: ctype = 'lltype.Void'	#lltype.py", line 177, in _inline_is_varsize  ->Void> cannot be inlined in structure
			else: raise NotImplemented
		else:
			if array:
				ctype = 'rffi.CFixedArray( %s, %s )' %( ctype, array )
				#pointers -= 1	# is this correct?

			if ':' in type and type.split(':')[0] == 'function':
				ctype = 'lltype.Ptr( %s )' %ctype

			elif pointers == 1 and not array:	# an array is the same as a pointer?
				if ':' in type: ctype = 'lltype.Ptr( %s )' %ctype
				else:
					#ctype = 'lltype.Ptr( %s )' %ctype		#TypeError: can only point to a Container type, not to Char
					ctype = 'rffi.CArrayPtr( %s )' %ctype

			elif pointers > 1:
				_a = 'rffi.CArrayPtr(' * pointers
				_a += ctype
				ctype = _a + ( ')'*pointers )

		return ctype


	#Usually, ctypes does strict type checking. This means, if you have POINTER(c_int) in the argtypes list of a function or as the type of a member field in a structure definition, only instances of exactly the same type are accepted. There are some exceptions to this rule, where ctypes accepts other objects. For example, you can pass compatible array instances instead of pointer types. So, for POINTER(c_int), ctypes accepts an array of c_int: - from the ctypes tutorial
	def ctypes_type( self ):		# TODO array for all types
		type = self.type()
		typedef = self.typedef()
		pointers = len(self.pointers())
		array = 0
		if self.array:
			print( 'array item name', self.name() )
			array += self.array.length()
		if typedef:
			pointers += len( typedef.pointers() )
			if typedef.array: array += typedef.array.length()

		ctype = None

		if ':' in type:
			if type.startswith('union:') or type.startswith('struct:'):
				if not self.name(): ctype = 'ctypes.c_void_p'	# opaque, un-named union or struct
				else: a,ctype = type.split(':')
			elif type.startswith('enum:'): ctype = 'ctypes.c_int'
			elif type.startswith('function:'):
				pointers -= 1		# it is funny how ctypes deals with callback functions, or is this only true with typedef'ed functions?
				if typedef and typedef.name() in SomeThing.Functions:
					ctype = SomeThing.Functions[typedef.name()].gen_ctypes_funcdef()
				else: ctype = 'ctypes.c_void_p'

		else:
			_type = type.split()
			if 'unsigned' in _type:
				unsigned = 'u'
				_type.remove( 'unsigned' )
			else: unsigned = ''
			if 'signed' in _type:
				assert not unsigned
				_type.remove( 'signed' )

			if _type.count( 'long' ) == 2: ctype = 'ctypes.c_%slonglong' %unsigned
			elif 'double' in _type and 'long' in _type: ctype = 'ctypes.c_longdouble'
			elif 'int' in _type and 'short' in _type: ctype = 'ctypes.c_%sint16' %unsigned	# correct?
			elif 'int' in _type and 'long' in _type: ctype = 'ctypes.c_%sint64' %unsigned	# correct?
			else:
				if len(_type) != 1:
					self.ast.show()
					print( 'WARN: this should never happen', _type )
					_type = ['void']

				type = _type[0]
				if type == 'void': ctype = 'ctypes.c_void_p'
				elif type == 'size_t': ctype = 'ctypes.c_size_t'
				elif type == 'ssize_t': ctype = 'ctypes.c_ssize_t'
				elif type == 'char' and unsigned: ctype = 'ctypes.c_ubyte'
				elif hasattr( ctypes, 'c_%s%s'%(unsigned,type) ):
					ctype = 'ctypes.c_%s%s'%(unsigned,type)		# should catch most types, even int16
				else:
					if type == 'uchar': ctype = 'ctypes.c_ubyte'
					else:
						print( 'WARN - bad type:', _type )
						raise NotImplemented

		if not ctype:
			self.ast.show()
			if self.type() == '<unknown-type>':
				ctype = 'ctypes.c_void_p'; print('TODO FIXME')
			else:
				print( self.type() )
				raise NotImplemented

		if array:		# most of the time its an array that may have pointers to it, not an array of pointers.
			ctype = '( %s * %s )' %( ctype, array )
		if pointers > 0:
			_a = 'ctypes.POINTER(' * pointers
			_a += ctype
			ctype = _a + ( ')'*pointers )

		return ctype


	def typedef( self ):
		if isclass( self.ast, 'IdentifierType' ):
			if self.ast.names:
				#name = self.ast.names[0]		# found bug may4th
				name = ' '.join( self.ast.names )
				if name in SomeThing.Typedefs: return SomeThing.Typedefs[ name ]
		elif self.child: return self.child.typedef()

	TYPEDEF_HACKS = {
		'int8_t'		:	'int8',
		'uint8_t'	:	'uint8',
		'int16_t'	:	'int16',
		'uint16_t'	:	'uint16',
		'int32_t'	:	'int32',
		'uint32_t'	:	'uint32',
		'int64_t'	:	'int64',
		'uint64_t'	:	'uint64',
	}
	def type(self):
		if isclass( self.ast, 'IdentifierType' ):
			if not self.ast.names: return '<unknown-type>'
			name = ' '.join(self.ast.names)
			#if name in SomeThing.Types: return SomeThing.Types[ name ].type()
			if name in SomeThing.Typedefs:
				#print( 'lookup typedef', name)
				# is this a bug in pycparser? see SDL_JoystickGetAxis - thinks its an int
				if name in self.TYPEDEF_HACKS:
					return self.TYPEDEF_HACKS[ name ]
				else:
					typedef = SomeThing.Typedefs[ name ]
					#print( typedef )
					#typedef.ast.show()
					return typedef.type()

			elif name in SomeThing.Types:	# is this safe, is it even used?
				_type = SomeThing.Types[ name ]
				if type(_type) is str: return _type
				else: return _type.type()

			elif name in SomeThing.MACRO_GLOBALS:
				#print( 'lookup macro global', name )
				type(SomeThing.MACRO_GLOBALS[ name ])
			elif name: return name
			else:
				self.ast.show(); print( self )
				raise SyntaxError
		elif isclass( self.ast, 'FuncDecl' ): return 'function:%s'	%self.name()
		elif isclass( self.ast, 'Struct', 'Union' ):
			n = self.name()
			if not n and self.parent: n = self.parent.name()		# nested problem
			if isclass( self.ast, 'Struct' ): return 'struct:%s'	%n
			else: return 'union:%s'	%n
		elif isclass( self.ast, 'Enum' ): return 'enum:%s' %self.name()
		elif self.child: return self.child.type()
		else: return '<unknown-type>'

################### end SomeThing ###############


class Array( SomeThing ):
	def length( self ):
		if self.ast.dim:
			val = self.ast.dim
			if isclass( val, 'Constant' ): return int(val.value)
			elif isclass( val, 'BinaryOp' ):
				r = Enum.compute_binaryop( val )
				if r is None:
					print( 'WARNING, can not find length of array' )
					self.ast.show()
					return 0
				else: return r
			elif isclass( val, 'ID' ):
				e = Enum.search_for_enum_with_key( val.name )
				if e: return e.values_by_key[ val.name ]
			else:
				print('-------------------')
				self.ast.show()
				raise NotImplementedError
		else:
			# zero length case
			return 0


class Enum( SomeThing ):		# an enum can be nameless
	@staticmethod
	def get_enum_value( name ): return SomeThing.Enums[ name ].enum_value( name )

	@staticmethod
	def search_for_enum_with_key( key ):
		for name in SomeThing.Enums:
			if key in SomeThing.Enums[name].values_by_key:
				return SomeThing.Enums[name]

	@staticmethod
	def compute_binaryop( binop ):
		#print binop.show()
		left = right = None
		if isclass( binop.right, 'Constant' ): right = int( binop.right.value, 16 )
		elif isclass( binop.right, 'ID' ): right = Enum.get_enum_value( binop.right.name )
		elif isclass( binop.right, 'BinaryOp' ): right = Enum.compute_binaryop( binop.right )

		if isclass( binop.left, 'Constant' ): left = int( binop.left.value, 16)
		elif isclass( binop.left, 'ID' ): left = Enum.get_enum_value( binop.left.name )
		elif isclass( binop.left, 'BinaryOp' ): left = Enum.compute_binaryop( binop.left )

		if left is not None and right is not None:
			e = '%s %s %s' %(left, binop.op, right)
			return eval( e )
		else:
			print('TODO unsupported binop:', binop)

	def enum_value( self, key ): return self.values_by_key[ key ]

	def _parse_constant( self, const ):
		value = const.value.strip()
		if const.type in 'int long'.split():
			if value.startswith('0x'):
				try: return int(value, 16)
				except: return int( value, 34 )	# base 2-36, is '0x0FFFFFFFU' base 34?

			elif value.endswith('U'): return int(value[:-1])	# what is ending with U?
			else: return int( value )
		elif const.type in 'float double'.split():
			if value.lower().endswith('f'): value = value[:-1]
			return float( value )
		elif value.startswith('"') or value.startswith("'"): return value[1:-1]
		else:
			print( const)
			print( const.type)
			print( const.value)
			raise NotImplementedError

	def setup(self):
		self.values = []
		self.values_by_key = {}
		if isclass( self.parent, 'FuncDef', 'FuncDecl' ) or not self.ast.values:
			self.ast.show()
			return
		self.ast.show()

		i = 0
		for val in self.ast.values.children():
			if val.value:
				print( val.value )
				if isclass( val.value, 'Constant' ):
					i = self._parse_constant( val.value )
				elif isclass( val.value, 'BinaryOp' ):
					i = self.compute_binaryop( val.value )
				elif isclass( val.value, 'UnaryOp' ):
					#print( dir(val.value) )
					#print( 'op', val.value.op, dir(val.value.op) )
					if isclass( val.value.expr, 'Constant' ):
						e = self._parse_constant( val.value.expr )
						i = eval( '%s %s' %(val.value.op,e) )
					elif isclass( val.value.expr, 'BinaryOp' ):
						i = self.compute_binaryop( val.value.expr )
					else:
						print( val.value.expr )
						raise NotImplementedError
				elif isclass( val.value, 'ID' ):
					print( dir(val.value) )
					if val.value.name in self.values_by_key: i = self.values_by_key[ val.value.name ]
					else:
						other = self.search_for_enum_with_key( val.value.name )
						if other:		# enums that reference other enums
							i = other.values_by_key[ val.value.name ]
						else:
							print( '--WARN enum not found--' ); val.show()
							raise NotImplementedError

				elif isclass( val.value, 'TernaryOp' ):
					print( 'TODO' ); val.show()
					#print( val.value )

				else:
					print('--unknown enum type--')
					print( 'TODO' ); val.show()
					print( val.value )
					raise NotImplementedError

			Enum.Enums[ val.name ] = self		# TODO raise if val.name already in Enums?
			self.values.append( (val.name, i) )
			self.values_by_key[ val.name ] = i
			#if not val.value:
			if type(i) == int: i += 1
			else: print( 'TODO non int incrementing enum')
			#if self.name() == 'WIIUSE_EVENT_TYPE': assert 0

	def gen_rffi(self, declare=False): return self._gen()
	def gen_ctypes(self, declare=False): return self._gen()
	def gen_rpython(self, declare=False): return self._gen().splitlines()

	def is_named_enum(self): return self.name()	# simple enum globals, or dict wrapped enum

	def _gen(self):
		r = ''
		if self.is_named_enum():	# wrap in dict
			n = self.name()
			if n.startswith('_') and n[1:] not in self.ReservedNames: n = n[1:]
			r = '%s = { \n' %n
			for v in self.values:
				name,value = v
				if type(value) is str: r += '\t"%s" : "%s", \n' %(name,value.replace('"', "'"))
				else: r += '\t"%s" : %s, \n' %(name,value)
			r += '}\n'
		else:			# define as simple globals
			for v in self.values:
				name,value = v
				if name.startswith('_') and name[1:] not in self.ReservedNames: name = name[1:]
				if type(value) is str: r += '%s = "%s" \n' %(name,value.replace('"', "'"))
				else: r += '%s = %s \n' %(name,value)
		return r

	def dict_format(self):
		r = ''
		for v in self.values:
			name,value = v
			if name.startswith('_') and name[1:] not in self.ReservedNames: name = name[1:]
			if type(value) is str: r += ' "%s" : "%s", ' %(name,value.replace('"', "'"))
			else: r += ' "%s" : %s, ' %(name,value)
		return r


class Function( SomeThing ):

	def setup(self):
		ast = self.ast
		#ast.show()
		#print 'function', ast.coord.file, ast.coord.line
		self.returns = None
		self.args = []
		self.body = None
		self.static = False
		args = None
		if isclass( self.ast, 'FuncDecl' ):
			decl = self.ast
			if ast.args: args = ast.args

		elif isclass( self.ast, 'FuncDef' ):
			decl = self.ast.decl
			#print( dir(decl) )
			if decl.storage and 'static' in decl.storage: self.static = True
			self.child = SomeThing( decl, parent=self )	# do not create sub-func type
			if decl.type.args: args = decl.type.args
			## TODO body
			if ast.body.block_items:
				self.body = True
				for node in ast.body.block_items:
					if isclass( node, 'Return' ): pass

		if args:
			for arg in args.children():
				if isclass( arg, 'EllipsisParam' ): pass #print 'EllipsisParam'	#TODO 
				else: self.args.append( SomeThing(arg, parent=self) )

		self.returns = SomeThing( decl.type, parent=self )		# re-enabled may16th
		#print( '-----------function---------', self.name() )
		#decl.show()
		#if isclass( decl.type, 'TypeDecl', 'PtrDecl' ):
		#	self.returns = SomeThing( decl.type.type, parent=self )
		#else:
		#	self.returns = SomeThing( decl.type, parent=self )
		#	#print('returns', decl.type)
		#print('----')
		#decl.type.show()
		#print('---------------------------')

		#if self.name() == 'SDL_JoystickGetAxis':		# ohno!
		#	self.ast.show()
		#	print( self.returns.type() )
		#	assert 0

	def gen_rffi( self, declare=False ):
		args = ''; name = self.name()
		if len(self.args)==1 and self.args[0].type() == 'void': pass
		else:
			for item in self.args: args += '%s,' %item.rffi_type()
		t = self.returns.type()
		#if ':' in _type:
		#	#res = 'lltype.Ptr(%s)' %res	#lltype.py", line 455, in __init__ raise TypeError, "function result can only be primitive or pointer"
		#	res = '%sPOINTER' %_type.split(':')[-1]
		#else:
		res = self.returns.rffi_type()
		if ':' in t and not self.returns.pointers():	# rffi requires return prim or return pointer
			print( 'WARNING', name, t )
			a,b = t.split(':')
			if a in 'union struct function'.split():	# enum is plain int
				if res != 'rffi.VOIDP':	# TODO fix me
					if res == 'lltype.Void': res = 'rffi.VOIDP'
					elif res.startswith('lltype.Ptr'): res = res + '#returns-> ' + t
					else: res = 'lltype.Ptr( %s )	# returns-> %s' %(res, t)

		return '%s = _c_external_function( "%s", [%s], %s \n)' %(name, name, args, res)

	def gen_ctypes_funcdef( self ):		# always redeclare func signature
		r = 'ctypes.CFUNCTYPE(%s, ' %self.returns.ctypes_type()
		if not (len(self.args)==1 and self.args[0].type() == 'void'):
			for item in self.args: r += '%s,' %item.ctypes_type()
		return r + ')'

	def gen_rffi_funcdef( self ):		# always redeclare func signature
		r = 'lltype.FuncType( ['
		if not (len(self.args)==1 and self.args[0].type() == 'void'):
			for item in self.args: r += '%s,' %item.rffi_type()
		r += '], %s )' %self.returns.rffi_type()
		return r

	def gen_ctypes( self, prototype=False ):
		name = self.name()
		if prototype:
			for arg in self.args:
				#assert arg.type() != '<unknown-type>'
				if arg.type() == '<unknown-type>':
					print( 'WARNING - bad function arg:', name )
					print( 'arg name: ', arg.name() )
					assert 0
			return 'PROTOTYPE_%s = %s' %(name, self.gen_ctypes_funcdef())

			if 0:
				s = 'PROTOTYPE_%s = ctypes.CFUNCTYPE(' %name
				if self.returns.type().startswith('function:'):	# bug fixed april 15th
					s += ' %s, ' %(self.returns.gen_ctypes(True))
				else: s += ' %s, ' %self.returns.ctypes_type()

				if len(self.args)==1 and self.args[0].type() == 'void': pass
				else:
					for item in self.args:
						if item.type().startswith('function:'):	# fixed april 15th
							s += '%s, ' %(item.gen_ctypes(True).split('=')[-1])
						else: s += '%s, ' %item.ctypes_type()
				return s + ')'


		elif self.static: return '# static function: %s'	%name
		else:
			args = ''
			if len(self.args)==1 and self.args[0].type() == 'void':
				if self.args[0].name(): args = '("%s",		ctypes.c_void_p)' %self.args[0].name()

			else:
				for item in self.args:
					_n = str(item.name())	# can return None
					_n = _n.replace('None', 'none')
					args += '\n\t("%s",		%s),' %(_n, item.ctypes_type())
			restype = self.returns.ctypes_type()
			return '%s = _rpythonic_function_(\t\t"%s", %s, [%s] )\n' %(name, name, restype, args )

	def gen_python( self ):
		if self.body:
			args = ''; name = self.name()
			if len(self.args)==1 and self.args[0].type() == 'void': pass
			else:
				for item in self.args: args += '%s,' %item.name()
			return [ 'def %s( %s ): pass' %(name,args) ]
		else: return []

ubug = None
class Union( SomeThing ):
	@staticmethod
	def mark_cyclic():
		r = []
		things = SomeThing.get_unions_and_structs()
		for p in things:
			for sub in p.substructures():
				for c in things:
					if c._name() == sub.type().split(':')[-1]:
						if c.contains_struct( p.type().split(':')[-1] ):
							sub.cyclic = True; r.append( c )
							print( 'found cyclic member', sub.name() )
						break
		return r

	def setup(self):
		ast = self.ast
		ast.show()
		self.fields = []
		self.forwardref = False
		self.contains_arrays = 0
		# SomeThing(a, parent=self) is this always safe with hacks like _name ?
		if ast.decls:
			self.fields = [ SomeThing(a, parent=self) for a in ast.decls ]
			for a in self.fields:
				if a.array: self.contains_arrays += 1

		else: self.forwardref = True

	def functions( self ):
		r = []
		for c in self.fields:
			type = c.type()
			if type.startswith('function:'): r.append( c )
		return r


	def substructures( self ):
		r = []
		for c in self.fields:
			type = c.type()
			if type.startswith('struct:') or type.startswith('union:'): r.append( c )
		return r

	def contains_struct( self, name ):
		for c in self.substructures():
			n = c.type().split(':')[-1]
			if n == name: return True
		return False


	def _name(self):		# fixes type def unions
		name = self.name()
		if not name and self.parent: name = self.parent.name()
		return name

	def gen_rffi( self, declare=False, forward=False, become=False ):		# no rffi.CUnion? in hints?
		name = self._name()
		if forward:
			return '%sPOINTER = lltype.Ptr(lltype.ForwardReference())' %name
		elif become:
			return '%sPOINTER.TO.become(%s)' %(name,name)
		elif declare: return '%s = rffi.CStruct( "%s" )' %(name,name)
		else:
			a = ''
			for item in self.fields:
				if item.name():
					t = item.type()
					n = item.name()
					rt = item.rffi_type()
					if ':' in t and t.split(':')[0]=='function' and rt.startswith('rffi.CFixedArray'):
						a += '\n\t\t( "%s", \t\trffi.VOIDP ),	#array of funcs warning#' %n
					else: a += '\n\t\t( "%s", \t\t%s ),' %(n, rt )

			return '_def_cstruct( %s, %s \n)'	%( name, a )

	def gen_ctypes( self, declare=False ):
		name = self._name()
		#altname = None		# changed may19th
		#if name.startswith('_') and name[1:] not in self.ReservedNames: altname = name[1:]
		if declare:
			## can not declare and pass, must also define _fields_ and append items to it,
			## otherwise ctypes reports: AttributeError: _fields_ is final
			## but can not declare with _fields_=[] and then append to it later - must resort to sorting??
			if self.tag == 'Union': a = 'class %s(ctypes.Union): pass' %name
			else:
				if self.contains_arrays: a = 'class %s(_rpythonic_struct_): _array_wrapper_ = True' %name
				else: a = 'class %s(_rpythonic_struct_): pass' %name
			#if altname: a += '\n%s = %s' %(altname,name)
			return a
		else:
			a = ''
			for item in self.fields:
				if not item.name():
					a += '\n\t#opaque-warning# %s' %item
				else: a += '\n\t( "%s", %s ),' %(item.name(), item.ctypes_type() )
			#return '%s._fields_=[%s\n]\n' %(name, a)
			return '__freeze_rpythonic_struct( %s, [%s\n])\n' %(name, a)

	def gen_python( self, declare=False  ):
		global TT
		r = [ 'class %s(object):	# <%s>' %(self._name(),self.tag) ]
		r.append( '\tdef __init__(self):' )
		for item in self.fields:
			TT = item
			type = item.type()
			r.append( '\t\tself.%s = %s' %(item.name(),type) )
		return r
TT = None

class Struct( Union ):
	#def gen_rffi( self, declare=False ):
	#	a = ''; name = self._name()
	#	for item in self.fields: a += '( "%s", %s ),' %(item.name(), item.rffi_type() )
	#	return '%s = rffi.CStruct( "%s", %s )' %(name, name, a )
	pass


class SourceCode(object):
	def __init__(self, url, debug=False, platform=None):
		path,name = os.path.split(url)
		if path not in INCLUDE_DIRS: INCLUDE_DIRS.append( path )
		self.source_url = url
		self.source_path, self.source_file = os.path.split( url )
		self.platform = platform

		## try to find compiled shared lib
		self.shared_library = None

		if LIBS: self.shared_library_name = os.path.splitext( os.path.split(LIBS[-1])[-1] )[0]
		else:
			if sys.platform.startswith('linux'):	# this only works in basic cases
				libname = os.path.splitext( self.source_file )[0] + '.so'
				if not libname.startswith('lib'): libname = 'lib' + libname
				guess1 = os.path.join( self.source_path, libname )
				guess2 = os.path.join( '/usr/local/lib', libname )
				guess3 = os.path.join( '/usr/lib', libname )
				if os.path.isfile( guess1 ): self.shared_library = guess1		# rare case
				elif os.path.isfile( guess2 ): self.shared_library = guess2	# user installed
				elif os.path.isfile( guess3 ): self.shared_library = guess3	# system
			if self.shared_library and self.shared_library not in LIBS: LIBS.append( self.shared_library )
			else: self.shared_library_name = '<undefined>'

		self.source_data = self.source_processed = open(url,'rb').read()
		self.headers = []
		self.includes = list( INCLUDE_DIRS )
		self.if_defs = []
		self.macro_globals = []
		self.macro_globals_values = {}
		## workaround
		SomeThing.MACRO_GLOBALS = self.macro_globals_values

		if '--no-preprocessor' not in sys.argv: self.source_processed = self.c_preprocessor()
		self.parse( self.source_processed, debug=debug )

	def save(self, name=None):
		#n = os.path.splitext(self.source_file)[0]
		#data = self.gen_python()		# TODO get this working
		#open('_python_%s.py'%n,'wb').write( data )
		if RFFI_OUTPUT:
			data = self.generate_rffi_wrapper()
			url = os.path.join( CACHEDIR, RFFI_OUTPUT )
			f = open(url,'wb'); f.write( data ); f.close()
			pprint('saved rffi wrapper: %s' %url, 2)
		if CTYPES_OUTPUT:
			print( 'saving ctypes wrapper: %s' %CTYPES_OUTPUT )
			data = self.generate_ctypes_wrapper()
			url = os.path.join( CACHEDIR, CTYPES_OUTPUT )
			f = open(url,'wb'); f.write( data ); f.close()
			pprint('saved ctypes wrapper: %s' %url, 2)

	def parse_macro( self, name, start, srclines ):
		if '(' in name or ')' in name: return False		# fixed april 15th
		if name == 'bool': return False						# fixed May3, openjpeg.h braindamage

		_clean = lambda line: line.split('/*')[0].split('//')[0].strip()
		n = start
		v = _clean( srclines[ n ] )
		while v.endswith('\\'):
			n += 1; v = v[:-1] + _clean( srclines[ n ] )
		line = v
		v = v.split()
		if name != v[1]:
			print( 'warn: function macro: %s' %name )
			print( line )
			return False
		if len(v) >=4:
			a = line.split('(')[-1]		# take last
			a = a.replace(')','')		# assume just closing pairs
			a = a.strip(); val = a
			try:
				val = eval( val )
				self.macro_globals.append( name )	# retain order
				self.macro_globals_values[ name ] = val
				return True
			except:
				print('HARD macro to parse:', line)

		elif len(v) >= 3:
			#print( 'saving', u )
			val = v[-1]
			val = val.split('(')[-1]
			if val.endswith(')'): val = val[:-1]
			if '.' in val and val.lower().endswith('f'): val = val[:-1]	# float litteral
			try:
				val = eval( val )
				self.macro_globals.append( name )	# retain order
				self.macro_globals_values[ name ] = val
				return True
			except:
				try:
					a = v[-1]; b = v[-2]
					if a in self.macro_globals_values:
						val = self.macro_globals_values[ a ]
						self.macro_globals.append( name )	# retain order
						self.macro_globals_values[ name ] = val
						return True
					elif a.count('(') == 1 and a.count(')') == 1:
						val = eval( a, self.macro_globals_values, self.macro_globals_values )
						self.macro_globals.append( name )	# retain order
						self.macro_globals_values[ name ] = val
						return True
					else: print( 'WARN: non trival macro', line ); return False

				except:
					pprint('WARNING: can not parse: %s'%name, 6)
					pprint( line, 1 )
					pprint('-'*80)
					return False
		else: pprint( 'not touching unused macro: %s' %name, 4 )


	def c_preprocessor( self, source_type=None ):
		source = self.source_data

		## insert extra headers ##
		if INSERT_HEADERS:
			insert = ''
			for header in INSERT_HEADERS:
				print( 'inserting header:', header )
				assert os.path.isfile( header )
				source += '\n' + open(header,'rb').read()

		all_includes = {}
		## first pass inspection, check includes, try to parse macros ##	
		for idx, line in enumerate(source.splitlines()):
			if line.startswith('#'):
				line = line[1:]
				line = line.split('//')[0]
				line = line.split('/*')[0]
				if line.startswith( 'define' ): self.parse_macro( line.split()[1], idx, source.splitlines() )	# march21,2011
				elif line.startswith('ifndef'): self.if_defs.append( line.split()[-1] )
				elif 'include' in line.split():
					name = line.split()[-1]
					if name.startswith('<') and name.endswith('>'):
						name = name.replace('<', '').replace('>','')
						all_includes[ name.split('/')[-1] ] = name
					else:
						name = name.replace('"', ''); all_includes[ name.split('/')[-1] ] = name
						path = find_header_directory( name, path=self.source_path )
						if path:
							if path not in self.includes: self.includes.append( path )
							if name not in self.headers: self.headers.append( name )
						else:
							print( 'WARNING: failed to find header directory', name )


		## post headers, just a hack to combine many headers ##
		#for header in HEADERS: source += '#include "%s"\n' %header			# TODO deprecate HEADERS

		## pre cpp - extra define hacks - why is this not working ##
		pre = [
			'#define const __const',
			'#define const __const__',
		]
		source = '\n'.join( pre ) + '\n' + source

		## always check for unused macro defines ##
		args = ['cpp']
		if self.__class__ == CPlusPlus:
			args.append( '-x' )
			args.append( 'c++' )
			#args.append( '-Ihacks/c++/' )
			args.append( '-C' )	# keep comments
		args.append( '-Wunused-macros' )

		if '--no-gnu' in sys.argv: args +=  ['-U__GNUC__' ]

		## avoid using fake libc
		if FAKE_LIBC: args.append( '-I%s' %FAKE_LIBC )

		if SYS_INCLUDE_DIRS:
			#args += '-nostdinc -nostdinc++'.split()		# cpp prints some warning
			args.append( '-nostdinc' )

		for path in self.includes: args.append( '-I%s' %path )
		for macro in MACRO_DEFS: args.append( '-D%s' %macro )
		for macro in MACRO_UNDEFS: args.append( '-U%s' %macro )
		
		args.append( '-' )
		print( '_'*80 )
		print( ' '.join( args ) )
		print( '_'*80 )
		pipe = subprocess.Popen( 
			args, 
			stdin=subprocess.PIPE, 
			stdout=subprocess.PIPE, 
			stderr=subprocess.PIPE, 
			universal_newlines=True)
		#print source
		data, err = pipe.communicate( input=source )		# input=None
		if not data.strip():
			print( err )
			raise SyntaxError

		unused_macros = []
		if err.strip():
			srclines = source.splitlines()
			err = err.strip()
			for line in err.splitlines():
				_line = line.split()
				if 'warning:' in _line:
					#print( line )
					if 'warning: macro' in line:
						u = line.split('"')[1]
						if u in '({' or u in self.macro_globals: continue	# fixed march22, 2011
						if u not in unused_macros:
							unused_macros.append( u )
							linenum = _line[0].split(':')[1]	# hopefully this won't change with other versions of cpp
							linenum = int( linenum )-1
							self.parse_macro( u, linenum, srclines )

				elif 'error:' in line.lower().split():
					print( '-'*80 ); print(err); print( '-'*80 )
					print( line )
					print( 'C pre-processor error! giving up.')
					raise SyntaxError

		# remove bad stuff pycparser can not handle #
		if self.__class__ is CPlusPlus: d = data
		else: d = make_pycparser_compatible( data )
		f = open('/tmp/_debug_%s' %self.source_file,'wb')
		f.write(d); f.close()
		return d


## data = output of C pre processor ##
def make_pycparser_compatible( data ):
	## deprecated ##
	d = ''
	TYPEDEF_HACKS = [ 
		('char *', '__builtin_va_list'),
		('unsigned char', '_Bool'),	# pypy generated, check for _GNUC_ and uses _Bool
	]
	if '--no-gnu' in sys.argv:
		TYPEDEF_HACKS.append(('long long int', 'int64_t'))
	for type, name in TYPEDEF_HACKS: d += 'typedef %s %s;\n' %(type,name)
	################ above is deprecated

	skip = False
	for num, line in enumerate(data.splitlines()):
		if skip:
			if line.strip().endswith(';'): line = ';'; skip = False
			else: continue

		if '((__noreturn__))' in line.split(): line = line.replace('((__noreturn__))','')	# pthread.h

		if line in ('char *__const__ * _PySequence_BytesToCharpArray(PyObject* self);', 'void _Py_FreeCharPArray(char *__const__ array[]);'):
			line = ''	# strange cases in python3.2/abstract.h

		if '__builtin_offsetof' in line.split() or '(__builtin_offsetof' in line.split():
			if 'pypy' in line: line = ';'	# pypy generated C
			else: line = line.replace('__builtin_offsetof', 'offsetof')

		if line.strip().startswith('__attribute__((format(printf,') and line.strip().endswith(')));'):
			line = ';'

		if '__attribute__((packed))' in line.split(): line = line.replace( '__attribute__((packed))', '' )

		if '__attribute__((malloc))' in line.split(): line = line.replace( '__attribute__((malloc))', '' )

		if '__attribute__((deprecated))' in line.split():	# alut.h
			line = line.replace( '__attribute__((deprecated))', '' )

		#if line.strip().endswith('__attribute__((__malloc__)) __attribute__((__alloc_size__(1)));'):	#glib/gslice.h:28
		#	line = line.replace( '__attribute__((__malloc__)) __attribute__((__alloc_size__(1)));', ';' )
		if '__attribute__((__malloc__))' in line.split() and line.strip().endswith(';'):
			line = line.split('__attribute__((__malloc__))')[0] + ';'

		if line.strip().endswith('__attribute__((warn_unused_result));'):
			line = line.replace('__attribute__((warn_unused_result));', ';' )
		if '__attribute__((warn_unused_result))' in line.split():
			_line = line.split('__attribute__((warn_unused_result))')[0]
			if line.strip().endswith(';'): _line += ';'
			line = _line

		if '((__malloc__));' in line.split(): line = line.replace('((__malloc__));', ';')	# stdio.h:225
		if '((__malloc__))' in line.split(): line = line.replace('((__malloc__))', '')

		if '__attribute__((visibility("default")))' in line.split():
			line = line.replace('__attribute__((visibility("default")))', '')

		if '__attribute__((__deprecated__))' in line.split():
			line = line.replace( '__attribute__((__deprecated__))', '' )	# ode/mass.h:69

		if '__extension__' in line.split(): line = line.replace('__extension__','')
		if '__attribute__' in line.split(): line = line.replace('__attribute__', '')
		if '__THROW' in line.split(): line = line.replace('__THROW', '')

		if '__inline__' in line.split(): line = line.replace( '__inline__', '' )
		if '__inline' in line.split(): line = line.replace('__inline', '' )	# odemath.h:316

		if '((__nothrow__))' in line.split(): line = line.replace('((__nothrow__))', '')	# inttypes.h
		if '((__nothrow__));' in line.split(): line = line.replace('((__nothrow__));', ';')

		if '((__const__))' in line.split(): line = line.replace('((__const__))', '')
		if '((__const__));' in line.split(): line = line.replace('((__const__));', ';')
		if '*__const' in line.split(): line = line.replace('*__const', '__const')	#unistd.h:541 sys_errlist.h
		if line.strip() == '((__const));': line = ';'	# /usr/include/ctype.h
		if '__const__' in line.split(): line = line.replace('__const__', 'const' )
		if '__const' in line.split(): line = line.replace('__const', 'const' )
		if '(__const' in line.split(): line = line.replace('(__const', '(const' )

		if '(__const__ ' in line: line = line.replace('(__const__', '(const' )
		if ',__const__' in line: line = line.replace( ',__const__', ',const' )

		if '***__restrict' in line.split(): line = line.replace('***__restrict', '***')	# dirent.h
		if '**__restrict' in line.split(): line = line.replace('**__restrict', '**')
		if '*__restrict' in line.split(): line = line.replace('*__restrict', '*' )
		if '__restrict' in line.split(): line = line.replace('__restrict', '' )

		if line.strip().startswith('((__format__ ('):		# stdio.h:385
			_s = line.strip()
			if _s.endswith( '))) ;' ) or _s.endswith(')));'): line = ';'

		if line.startswith('extern') and '((visibility("default")))' in line.split():		# SDL_cdrom.h
			line = line.replace( '((visibility("default")))', '' )

		# types.h #
		for ugly in ['((__mode__ (__QI__)));' , '((__mode__ (__HI__)));' , '((__mode__ (__SI__)));' , '((__mode__ (__DI__)));' , '((__mode__ (__word__)));']:
			if line.endswith( ugly ): line = line.replace(ugly, ';')

		if '__asm__' in line.split(): #and '__isoc99_' in line:
			if line.strip().endswith(';'): line = line.split('__asm__')[0] + ';'
			else: line = line.split('__asm__')[0]


		if '((__nonnull__' in line.split():		#unistd.h
			if line.strip().endswith(')));') or line.strip().endswith('))) ;') or line.strip().endswith('((__deprecated__)) ;'):
				line = line.split('((__nonnull__')[0] + ';'
			elif line.strip().endswith(';'):
				line = line.split('((__nonnull__')[0] + ';'	# this is always safe?

		if line.strip().endswith('((__noreturn__));'): line = line.replace('((__noreturn__));', ';')	#unistd.h:596 _exit
		if '((__pure__))' in line.split():		# string.h:45
			if line.strip().endswith(';'): line = line.split('((__pure__))')[0] + ';'
		if line.strip().endswith('__attribute__((__pure__));'):
			line = line.replace('__attribute__((__pure__));', ';')
		if line.strip().startswith('__attribute__((visibility("hidden")))'):	#glib-2.0/glib/gmessages.h:97
			line = line.replace('__attribute__((visibility("hidden")))', '')
		if line.strip().endswith('__attribute__;'):	# glib-2.0/glib/gmessages.h:113
			line = line.replace('__attribute__;', ';')
		if '__attribute__((__format_arg__' in line.split() and line.strip().endswith(';'):
			line = line.split('__attribute__((__format_arg__')[0] + ';'

		if line.strip().endswith('__attribute__((const));'): line = line.replace('__attribute__((const));', ';')	#glib-2.0/glib/gquark.h

		#if '__const struct' in line:	# time.h:187, this can go away, will pop __const in _fix_decl_name_type
		#	print('WARNING: %s' %line)
		#	line = line.replace( '__const struct', 'struct' )

		if line.strip().endswith( '((__transparent_union__));' ):	# stdlib.h:72
			line = line.replace( '((__transparent_union__));', ';' )

		if line.strip().endswith( '((__warn_unused_result__));' ):
			line = line.replace('((__warn_unused_result__));', ';' )	# stdlib.h:400

		if line.strip().endswith('__attribute__((__const__));'):	# glib-2.0/glib/gquark.h:39
			line = line.replace('__attribute__((__const__));', ';' )

		if '__attribute__((__format__' in line.split() and line.strip().endswith('));'):
			line = line.split('__attribute__((__format__')[0] + ';'

		if line.strip().endswith('__attribute__((__malloc__));'):
			line = line.replace( '__attribute__((__malloc__));', ';')

		if '((unused))' in line.split():	# glib-2.0/glib/gutils.h:300
			line = line.replace('((unused))', '')

		if '__attribute__((may_alias))' in line.split():	# glib-2.0/glib/gatomic.h:38
			line = line.replace('__attribute__((may_alias))', '')

		if line.strip().endswith('((__deprecated__));'):	#/usr/include/signal.h:196
			line = line.replace('((__deprecated__));', ';')

		if line.strip().endswith('__attribute__ ;'):		#/usr/include/bits/sigthread.h:33
			line = line.replace('__attribute__ ;', ';')

		if line.strip().endswith('__attribute__((__sentinel__));'):	#glib-2.0/gobject/gobject.h:414
			line = line.replace('__attribute__((__sentinel__));', ';')

		if line.strip().endswith('((__aligned__));'):
			line = line.replace('((__aligned__));', ';')

		if '((__regparm__' in line.split():	# pthread.h:665
			if line.strip().endswith(';'):
				line = line.split('((__regparm__')[0] + ';'
			elif line.strip().startswith('((__regparm__'): line = ''
		if line.strip() == '((__weak__))': line = ''

		if line.startswith('#pragma'): line = ''

		if line.startswith('extern int __sigsetjmp (struct __jmp_buf_tag') and line.endswith(';'):
			line = 'extern int __sigsetjmp ();'

		#if line == 'struct XnMutex;': line = 'typedef struct XnMutex;'
		if line == 'typedef XnMutex* XN_MUTEX_HANDLE;':	# strange that forcing these typedefs to int works
			line = 'typedef int XN_MUTEX_HANDLE;'
		if line == 'typedef XN_MUTEX_HANDLE XN_CRITICAL_SECTION_HANDLE;':
			line = 'typedef int XN_CRITICAL_SECTION_HANDLE;'

		if line.strip() == '((format (printf, 3, 4)));' or line.strip()=='((format (printf, 1, 2)));': line = ';'	# for blender.h

		if line.strip() == '((__pure__));': line = ';'	# /usr/include/wchar.h
		if line.strip().endswith('((__pure__));'): line = line.replace('((__pure__));', ';')

		## pypy hybrid gc creates this funny line in pypy_g_ll_arena_round_up_for_allocation
		#  l_v3659 = ((((l_arg0_6)>=(l_arg1_6)?(l_arg0_6):(l_arg1_6)) + (offsetof (struct rpy_memory_alignment_test2, s)-1)) & ~(offsetof (struct rpy_memory_alignment_test2, s)-1));
		if '& ~(offsetof (struct rpy_memory_alignment_test2' in line and line.strip().endswith(';'):
			line = ';'

		if line.strip().startswith('asm volatile') and line.strip().endswith(';'): line = ';'		# for boehm gc


		if line.strip().startswith('__asm__'):
			if line.strip().endswith(';'): line = ';'
			else: line = ''; skip = True

		if '__asm(' in line and line.strip().endswith(');'):	# darwin - stdio.h
			line = line.split('__asm(')[0] + ';'

		if '({ union { uint8_t b8[2]; uint16_t b16; } _tmp; uint16_t _tmp2 = (uint16_t)' in line: line = ';'	#libusb-1.0/libusb.h:886

		## android include/machine/_types.h		include/asm/types.h
		#if line.strip() == 'typedef __int8_t __int_least8_t;': line = 'typedef signed char __int_least8_t;'
		#if line.strip() == 'typedef __int8_t int8_t;' : line = 'typedef signed char int8_t;'
		if '__signed__' in line.split(): line = line.replace( '__signed__', 'signed' )
		if '__signed' in line.split(): line = line.replace( '__signed', 'signed' )

		if '__OSX_AVAILABLE_STARTING(' in line and line.endswith(';'):
			line = line.split( '__OSX_AVAILABLE_STARTING(' )[0] + ';'
		if line.strip() in ['__asm("_" "pselect" )', '__asm("_" "select" )']:		# darwin - select.h
			line = ''
		if '__OSX_AVAILABLE_BUT_DEPRECATED(' in line:
			if line.strip().endswith(';'):
				line = line.split('__OSX_AVAILABLE_BUT_DEPRECATED(')[0] + ';'
			else:
				line = line.split('__OSX_AVAILABLE_BUT_DEPRECATED(')[0]
				skip = True

		if line.strip().startswith('((format (printf,') and line.strip().endswith(')))'):
			line = ''

		#######################
		d += line + '\n'
	return d

TRANS_HEADER = '''

def printf( arg ): print( arg )

'''



class Emscripten(object):
	'''
	The old PyPy javascript backend would not likely produce better results than Emscripten+Closure,
	because PyPy's flowgraph is already low level, and the old js backend was not optimized for strict-javascript.
	The other advantage of Emscripten is translation of C libraries used via RFFI becomes possible.

	INSTALLING:
		sudo apt-get install llvm-gcc-4.2

	Ubuntu 64bit:
		not really supported, for the brave see...
		http://mdqinc.com/blog/2011/05/from-python-to-javascript-the-scenic-route/

	'''

	_config_ubuntu_mavrick = '''
TEMP_DIR='/dev/shm'
LLVM_ROOT=os.path.expanduser('/usr/bin')
LLVM_GCC=os.path.expanduser('/usr/bin/llvm-g++-4.2')
COMPILER_OPTS = ['-m32']
path = os.path.split(os.path.abspath(__file__))[0]
rel = os.path.join( path, '../../bin/linux32/d8' )
V8_ENGINE = [  os.path.abspath(rel)  ]
COMPILER_ENGINE=V8_ENGINE
JS_ENGINE=V8_ENGINE
TIMEOUT = None
CLOSURE_COMPILER = None
SPIDERMONKEY_ENGINE = []
'''

	def _ensure_setup(self, force=True):	# if you are doing your own custom config, disable force
		cfg = os.path.expanduser('~/.emscripten')
		if force or not os.path.isfile(cfg):
			f = open( cfg, 'wb' ); f.write( self._config_ubuntu_mavrick )
			f.close()


	def __init__( self, paths=[], source=None, includes=[], defs=[], links=[], cplusplus=False, optimize=2, pthread=True, glibc=True, gc=None ):
		self._ensure_setup()
		emmaken = os.path.join( RPYTHONIC_DIR, 'emscripten/tools/emmaken.py' )

		assert paths or source
		if not paths:
			if cplusplus: paths = ['/tmp/_compile_.cpp']
			else: paths = ['/tmp/_compile_.c']
			f = open(paths[0],'wb')
			f.write( source ); f.close()

		## TODO needs 64bit support ##
		if '/usr/include/i386-linux-gnu/' not in includes: includes.append( '/usr/include/i386-linux-gnu/' )

		a = ''
		for x in includes: a += '-I%s ' %x
		b = ''
		for x in defs: b += '-D%s ' %x

		asmobjects = []
		for path in paths:
			# -c (do not run linker)
			n = os.path.splitext(os.path.split(path)[-1])[0]
			asmob = '/tmp/%s.o' %n
			asmobjects.append( asmob )
			cmd = '%s -c %s ' %(emmaken, path)
			if optimize: cmd += ' -O%s ' %optimize
			if pthread: cmd += ' -pthread '
			cmd += '-o %s %s %s' %(asmob,a,b)
			print( cmd )
			os.system( cmd )


		cmd = '%s ' %emmaken
		if pthread: cmd += ' -pthread '
		if glibc: cmd += ' -lrt '
		if gc=='boehm': cmd += ' -lgc '

		for lib in links:
			cmd += ' -l%s ' %lib		# name without path or 'lib' prefix or '.so' extension

		cmd += '-o /tmp/_emmaken_temp '
		for asmob in asmobjects: cmd += ' %s ' %asmob
		print( cmd )
		os.system( cmd )

		cmd = 'llvm-dis -show-annotations /tmp/_emmaken_temp'
		print( cmd )
		os.system( cmd )
		self._raw_ll = open( '/tmp/_emmaken_temp.ll', 'rb' ).read()

		remdead = os.path.join( RPYTHONIC_DIR, 'emscripten/tools/dead_function_eliminator.py' )
		cmd = 'python %s /tmp/_emmaken_temp.ll /tmp/_optimized_.ll' %remdead
		print( cmd )
		os.system( cmd )
		self._optimized_ll = open( '/tmp/_optimized_.ll', 'rb' ).read()

		#data = open( '/tmp/_emmaken_temp.ll', 'rb' ).read()
		#return data
		emscripten = os.path.join( RPYTHONIC_DIR, 'emscripten/emscripten.py' )
		d8 = os.path.join( RPYTHONIC_DIR, 'bin/linux32/d8' )
		cmd = '%s /tmp/_optimized_.ll %s > /tmp/_javascript_pass1' %(emscripten, d8)
		print( cmd )
		os.system( cmd )
		self._raw_js = open( '/tmp/_javascript_pass1', 'rb' ).read()

		closure = os.path.join( RPYTHONIC_DIR, 'bin/Closure/compiler.jar' )
		# ADVANCED_OPTIMIZATIONS runs out of RAM on a 2GB machine!!!!
		cmd = 'java -jar %s --compilation_level SIMPLE_OPTIMIZATIONS --formatting PRETTY_PRINT --js=/tmp/_javascript_pass1 --js_output_file=/tmp/_javascript_pass2' %closure
		print( cmd )
		os.system( cmd )
		data = open( '/tmp/_javascript_pass2', 'rb' ).read()
		self._optimized_js = 'function _write(ptr, value, num) {return _memset(ptr, value, num);};\n' + data

	def get_source(self): return self._optimized_js

	def test( self ):
		print( '-'*80)
		open( '/tmp/_test_.js', 'wb').write( self._optimized_js )
		d8 = os.path.join( RPYTHONIC_DIR, 'bin/linux32/d8' )
		os.system( '%s /tmp/_test_.js' %d8 )
		print( '-'*80)


class GCC(object):
	'''
       -ffast-math
           Sets -fno-math-errno, -funsafe-math-optimizations,
           -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans and
           -fcx-limited-range.

           This option causes the preprocessor macro "__FAST_MATH__" to be
           defined.

           This option is not turned on by any -O option since it can result
           in incorrect output for programs which depend on an exact
           implementation of IEEE or ISO rules/specifications for math
           functions. It may, however, yield faster code for programs that do
           not require the guarantees of these specifications.

       -Os Optimize for size.  -Os enables all -O2 optimizations that do not
           typically increase code size.  It also performs further
           optimizations designed to reduce code size.

       -O3 Optimize yet more.  -O3 turns on all optimizations specified by -O2
           and also turns on the -finline-functions, -funswitch-loops,
           -fpredictive-commoning, -fgcse-after-reload and -ftree-vectorize
           options.


       -freciprocal-math
           Allow the reciprocal of a value to be used instead of dividing by
           the value if this enables optimizations.  For example "x / y" can
           be replaced with "x * (1/y)" which is useful if "(1/y)" is subject
           to common subexpression elimination.  Note that this loses
           precision and increases the number of flops operating on the value.

           The default is -fno-reciprocal-math.


      -pthreads
           Add support for multithreading using the POSIX threads library.
           This option sets flags for both the preprocessor and linker.  This
           option does not affect the thread safety of object code produced
           by the compiler or that of libraries supplied with it.

       -pthread
           This is a synonym for -pthreads.

       -msmall-mem
       -mlarge-mem
           By default, GCC generates code assuming that addresses are never
           larger than 18 bits.  With -mlarge-mem code is generated that
           assumes a full 32 bit address.


	-freg-struct-return
		       Return "struct" and "union" values in registers when possible.
		       This is more efficient for small structures than
		       -fpcc-struct-return.

	'''
	@staticmethod
	def compile( name, paths=[], source=None, includes=[], defines=[], links=[], cplusplus=False, optimize=2, pthread=True, glibc=True, gc=None, warnings=True, runpath=None ):
		assert paths or source
		if not paths:
			if cplusplus: paths = ['/tmp/_compile_.cpp']
			else: paths = ['/tmp/_compile_.c']
			f = open(paths[0],'wb')
			f.write( source ); f.close()

		a = ''
		for x in includes: a += '-I%s ' %x
		b = ''
		for x in defines: b += '-D%s ' %x

		asmobjects = []
		for path in paths:
			# -c (do not run linker)
			n = os.path.splitext(os.path.split(path)[-1])[0]
			asmob = '/tmp/%s.o' %n
			asmobjects.append( asmob )
			cmd = 'gcc -c -fPIC %s ' %path
			if optimize: cmd += ' -O%s ' %optimize
			if pthread: cmd += ' -pthread '
			if not warnings: cmd += ' -w '
			cmd += '-o %s %s %s' %(asmob,a,b)
			print('-'*80)
			print( cmd )
			print('-'*80)
			os.system( cmd )
			if not os.path.isfile( asmob ): return None

		cmd = 'gcc -shared -Wl,-soname,lib%s.so ' %name
		if runpath: cmd += '-Wl,-rpath %s -Wl,--export-dynamic ' %runpath
		if pthread: cmd += ' -pthread '
		if glibc: cmd += ' -lrt '
		if gc=='boehm': cmd += ' -lgc '

		for lib in links:
			cmd += ' -l%s ' %lib		# name without path or 'lib' prefix or '.so' extension, ie: 'OgreMain' not libOgreMain.so
		if IS32BIT: cachepath = os.path.join(CACHEDIR,'clibs/linux32')
		else: cachepath = os.path.join(CACHEDIR,'clibs/linux64')
		if not os.path.isdir(cachepath): os.makedirs( cachepath )
		libpath = os.path.join( cachepath, 'lib%s.so'%name )
		cmd += '-o %s ' %libpath
		for asmob in asmobjects: cmd += ' %s ' %asmob
		print( cmd )
		os.system( cmd )
		print('-'*80)
		#try:
		lib=ctypes.CDLL(libpath, mode=ctypes.RTLD_GLOBAL)
		print( lib )
		print('-'*80)
		print( 'compile passed, linker passed, and loaded by ctypes passed' )
		print('_'*80)

		#except:
		#	print( 'ERROR: generated lib is failed to load.' )
		#	assert 0
		return lib

############### new c++ ############### 
def has_valid_name( name, allow_namespace=False ):
	test = '()[]{}<>^|=~!*+-'
	if not allow_namespace: test += ':'
	for x in test:
		if x in name: return False
	else: return True


class CPP_variable( object ):
	def __init__(self,var=None, name=None, type=None, parent=None):
		self.parent = parent
		self.basicinfo = ''
		if var:
			self.info = var
			self.name = self.info['name']
			self.type = self.info['raw_type']
			self.fundamental = self.info['fundamental']

			for n in 'type raw_type class constant reference pointer static typedef template fundamental unresolved array enum default ctypes_type struct nested_struct nested_typedef concrete_type aliases'.split():
				if n in var.keys():
					if var[n]: self.basicinfo += '%s: %s, ' %(n,var[n])

		else:
			self.info = {}
			self.name = name
			self.type = type
		self.name = self.name.strip()
		#self.valid = has_valid_name( self.name, allow_namespace=True )
		self.valid = True

		self.ctype = self.cpptype = self.type
		if not self.fundamental: self.ctype = 'void*'

	def is_fundamental( self ): return self.fundamental


class CPP_method( object ):
	UNSAFE_UNKNOWN_RETURNS = (
		'QVariant',
		'QVector2D',
		'QVector4D',
		'boost::weak_ptr<T>',
		'boost::shared_ptr<T>',
		'std::vector<boost::shared_ptr<T> >',
		'ServiceManagerPtr',	# Tundra
		'ModuleManagerPtr',	# Tundra
		'T',				# Tundra
		'entity_id_t',		# Tundra
		'boost::int64_t',
		'btNearCallback',		# bullet
	)
	UNSAFE_TYPEDEF_RETURNS = (
		'T *',				# Tundra
		'const_iterator',
		'ValueType',		# Ogre
	)
	UNSAFE_RETURNS = (
		'QString::iterator',
		'QString::const_iterator',
		'QByteArray::const_iterator',
		'QByteArray::iterator',
		#'return',	# this is a CppHeaderParser bug
		'__inline boost::weak_ptr<T>',	# Tundra
	)
	UNSAFE_NESTED_RETURNS = ()

	def __init__(self, meth, parent=None):
		self.info = meth
		#for tag in 'defined pure_virtual operator constructor destructor extern template virtual static explicit inline friend returns returns_pointer returns_fundamental returns_class'.split():
		#	setattr(self, tag, meth[tag])
		self.name = meth['name']
		self.debug = meth['debug']

		############ hacks ##############
		if meth['returns'] == '__inline void':
			meth['returns'] = 'void'
			meth['returns_fundamental'] = True
		elif meth['returns'] == 'GLenum':
			meth['returns'] = 'int'
			meth['returns_fundamental'] = True
			meth['returns_ctypes'] = 'ctypes.c_int'


		############# end hacks #############

		self.returns = meth['returns']

		self.arguments = []
		self.parent = parent
		if parent: self.namespace = parent.namespace
		else: self.namespace = meth['namespace']
		if self.namespace.endswith('::'): self.namespace = self.namespace[:-2]

		if meth['pure_virtual']: self.valid = False
		else: self.valid = True

		self.requires_template_typename = False

		if len( meth['parameters'] ) == 1 and meth['parameters'][0]['type']=='void':
			pass
		else:
			for arg in meth[ 'parameters' ]:
				if not arg or (not arg['name'] and arg['type']!='void'):
					self.valid = False
					print('warning - method had unparseable argument:', self.name)

				elif arg:

					# hacks for Tundra - TODO fix me
					#if 'enum' in arg and arg['enum']=='Service::Type':
					#	arg['type'] = arg['raw_type'] = 'AttributeChange::Type'
					#elif arg['raw_type']=='Scene::QDomDocument': arg['raw_type'] = 'QDomDocument'
					#elif arg['raw_type']=='Scene::QDomElement': arg['raw_type'] = 'QDomElement'
					# end of hacks for Tundra
					#if 'String' in arg['aliases']: print(arg); assert 0
					#if 'StringStream' in arg['raw_type']: print(arg); assert 0

					self.arguments.append( CPP_variable(arg, parent=self) )
					#self.valid = self.arguments[-1].valid
					#if not self.valid: break

					if arg['raw_type'] == '(template)' or 'template_requires_typename' in arg:
						 
						self.requires_template_typename = True

				else:
					assert 0
		#if self.valid: self.valid = has_valid_name( self.name )
		if 'returns_unknown' in meth and meth['returns'] in self.UNSAFE_UNKNOWN_RETURNS:
			self.valid = False

		# t + = QChar : : fromAscii ( c ) ;	# Qt/Tundra bug
		#if '=' in meth['returns']: self.valid = False
		#if '* this>' in meth['returns']: self.valid = False	# return * this > QString : : fromAscii ( s ) ; BUG
		if 'returns_typedef' in meth and meth['returns_typedef'] in self.UNSAFE_TYPEDEF_RETURNS:
			self.valid = False
		if meth['returns'] in self.UNSAFE_RETURNS:
			self.valid = False
		elif 'returns_nested' in meth and meth['returns_nested'] in self.UNSAFE_NESTED_RETURNS:
			self.valid = False
		elif 'returns_invalid' in meth:
			self.valid = False




class CPP_class( object ):
	CLASS_NAMES = []

	PYTHON_OPERATOR_MAP = {
		'()'	:	'__call__',
		'[]'	:	'__getitem__',
		'<'	:	'__lt__',
		'<='	:	'__le__',
		'=='	:	'__eq__',
		'!='	:	'__ne__',
		'>'	:	'__gt__',
		'>='	:	'__ge__',
		'+'	:	'__add__',
		'-'	:	'__sub__',
		'*'	:	'__mul__',
		'%'	:	'__divmod__',
		'**'	:	'__pow__',
		#'>>'	:	'__lshift__',		# TODO
		'<<'	:	'__rshift__',
		'&'	:	'__and__',
		'^'	:	'__xor__',
		'|'	:	'__or__',
		'+='	:	'__iadd__',
		'-='	:	'__isub__',
		'*='	:	'__imult__',
		'/='	:	'__idiv__',
		'%='	:	'__imod__',
		'**='	:	'__ipow__',
		'<<='	:	'__ilshift__',
		'>>='	:	'__irshift__',
		'&='	:	'__iand__',
		'^='	:	'__ixor__',
		'|='	:	'__ior__',
		#__neg__ __pos__ __abs__; what are these in c++?
		'~'	:	'__invert__',
		'.'	:	'__getattr__',
	}

	def __init__(self, klass):
		self.info = klass
		self.name = self.classpath = klass['name']

		self.namespace = klass['namespace']
		if self.namespace:
			self.classpath = self.namespace + '::' + self.name
			if self.classpath.startswith('::'): self.classpath = self.classpath[2:]	# rare - ::EC_Name::EC_NameFactory

		if 'RenderQueueInvocationList' in self.classpath: assert 0

		self.inherits = []
		for d in klass['inherits']:
			#if d['access'] == 'public':
			p = d['class'].replace('::','.')
			if p in self.CLASS_NAMES:
				self.inherits.append( p )
			else:
				print( 'WARN: missing inherits from class', p )

		self.abstract = klass['abstract']
		self.parent = klass['parent']
		self.invalid = False
		self.unsafe_template = 'complex_template' in self.info or 'special_template' in self.info or 'unsafe_template' in self.info
		##################################################################################

		self.methods = []		# only interested in public?
		self.constructors = []
		self.destructors = []
		self.properties = []
		self.operators = []

		for m in klass['methods']['public']:
			meth = CPP_method( m, parent=self )
			if not meth.valid: continue

			if meth.info['operator'] in self.PYTHON_OPERATOR_MAP and len(meth.arguments) == 1 and 'returns_unknown' not in meth.info:
				if 'enum' in meth.arguments[0].info: continue	# skip for now. TODO
				else: self.operators.append( meth )
			elif meth.info['operator']: continue
			elif 'returns_concrete' in meth.info and meth.info['returns_concrete'].startswith('typename'):
				continue

			if meth.info['constructor']: self.constructors.append( meth )
			elif meth.info['destructor']: self.destructors.append( meth )
			elif meth.info['name'] == self.name: pass		# what are these, copy constructors?
			else: self.methods.append( meth )
			#if meth.valid:
			#	if meth.name == self.name: self.constructors.append( meth )

		for p in klass['properties']['public']:
			if not p['unresolved'] and p['fundamental']:
				self.properties.append( CPP_variable(p) )

		if not self.invalid:
			if self.name not in self.CLASS_NAMES: self.CLASS_NAMES.append( self.name )
		

	NumFuncs = 0
	def gen_wrapper( self, gen_func_names ):
		#m = {'class': self.name, 'classpath':self.classpath}
		#_py_constructors = []
		#_py_properties = {}
		#_py_methods = {}
		vclasses = {
			None:{'name':self.name, 'classpath':self.classpath, 'constructors':[], 'methods':{}, 'properties':{}}
		}
		def get( tag=None ):
			if not tag: return vclasses[None]
			elif not tag['type'] in vclasses:
				name = tag['name'].split('::')[-1]
				path = tag['type'].replace('<','_').replace('>','_').replace(' ','_')
				vclasses[ tag['type'] ] = {'name':name, 'classpath':path, 'constructors':[], 'methods':{}, 'properties':{}}
			return vclasses[ tag['type'] ]


		cpp = [ 
			'/////////////////////////////////////////////////////////////////////////////',
			'/* --------------- class %s --------------- */' %self.name,
			'/*	<abstract: %s> */' %self.abstract,
		]
		for parent in self.info['inherits']:
			cpp.append( '/*	<parent class: %s> */' %parent['class'] )

		if 'template_info' in self.info:
			cpp.append( '/*	<template: %s> */' %self.info['template_info'] )


		if 'doxygen' in self.info and self.info['doxygen']:
			dox = self.info['doxygen'].replace('/*','').replace('*/','')
			cpp.append( '/*%s*/' %dox )


		########### CONSTRUCTOR/DESTRUCTOR ############
		if not self.abstract:

			if 'template_info' in self.info:

				for typename in self.info['template_info']:
					T = self.info['template_info'][ typename ]
					name = self.classpath+'__template__'+typename
					path = T['type']

					print( T )
					if not T['fundamental']: continue

					if self.destructors:
						## assume destructor no args - delete safe ##
						n = '%s__template__%s__delete__' %(self.classpath.replace('::','_'), typename)
						cpp.append( 'void %s(void* obj) { delete (%s*)(obj); } //TEMPLATE ' %(n, T['type']) )


					for i, func in enumerate(self.constructors):
						build_wrapper_constructor( i, func, cpp, get(T)['constructors'], path=path, name=name, template=T )

			elif 'template_typename' in self.info:
					cpp.append( '/* TODO complex template: %s */' %self.classpath )

			else:
				if self.destructors:
					## assume destructor no args - delete safe ##
					n = '%s__delete__' %self.classpath.replace('::','_')
					cpp.append( 'void %s(void* obj) { delete (%s*)(obj); } ' %(n, self.classpath) )

				for i, func in enumerate(self.constructors):
					build_wrapper_constructor( i, func, cpp, get()['constructors'], path=self.classpath )


		################ PROPERTIES ###################
		if not 'template_typename' in self.info:
			cpp.append( '/* ---------properties--------- */' )
			for prop in self.properties:
				build_wrapper_property( prop, cpp, get()['properties'], self.classpath )



		################## METHODS ####################

		for meth in self.methods:
			if not meth.valid: cpp.append( '/* %s.%s <invalid> */' %(self.classpath, meth.name) ); continue
			elif meth.info['explicit']: cpp.append( '/* %s.%s <explicit> */' %(self.classpath, meth.name) ); continue
			#elif meth.info['static']: cpp.append( '/* %s.%s <static> */' %(self.classpath, meth.name) ); continue
			elif 'unresolved_parameters' in meth.info:
				cpp.append( '/* %s.%s <UNRESOLVED PARAMETERS>' %(self.classpath, meth.name) )
				for arg in meth.arguments: cpp.append( arg.basicinfo )
				cpp.append( '*/' )
				continue
			elif ('template' in meth.info and meth.info['template']) or 'template_info' in self.info:
				cpp.append( '/* %s.%s <template> */' %(self.classpath, meth.name) )
				if 'template_info' not in self.info:
					if meth.requires_template_typename: continue

					gname = '%s_%s' %(self.classpath.replace('::','_'), meth.name)
					if gname not in gen_func_names: gen_func_names[ gname ] = 0
					gen_func_names[ gname ] += 1
					if gen_func_names[ gname ] > 1: gname += str( gen_func_names[gname] )
					cpp.append( '/* %s.%s */' %(self.classpath, meth.name) )
					build_wrapper_function( meth, gname, cpp, get()['methods'], path=self.classpath )

					continue

				for typename in self.info['template_info']:
					T = self.info['template_info'][ typename ]

					if not T['fundamental']: continue

					gname = self.classpath.replace('::','_')+'__template__%s_%s' %(typename,meth.name)
					if gname not in gen_func_names: gen_func_names[ gname ] = 0
					gen_func_names[ gname ] += 1
					if gen_func_names[ gname ] > 1: gname += str( gen_func_names[gname] )

					path = T['type']; print path
					build_wrapper_function( meth, gname, cpp, get(T)['methods'], path=path, template=T )

			elif 'template_typename' in self.info and not 'template_info' in self.info:
				cpp.append( '/* %s.%s <skipping complex template: %s> */' %(self.classpath, meth.name,self.info['template_typename']) )

			else:
				if meth.requires_template_typename: print('no supposed to happen here'); continue

				gname = '%s_%s' %(self.classpath.replace('::','_'), meth.name)
				if gname not in gen_func_names: gen_func_names[ gname ] = 0
				gen_func_names[ gname ] += 1
				if gen_func_names[ gname ] > 1: gname += str( gen_func_names[gname] )
				cpp.append( '/* %s.%s */' %(self.classpath, meth.name) )
				build_wrapper_function( meth, gname, cpp, get()['methods'], path=self.classpath )


		############################# PYTHON ############################

		py = [ ]
		for a in vclasses.values():
			py += [
				'#------------------ %s ------------------#' %a['classpath'],
				'@meta( ',
				'	constructors=%s,' %a['constructors'],
				'	methods=%s,' %a['methods'],
				'	properties=%s' %a['properties'],
				')',
			]

			if self.inherits:
				py.append( 'class %s( %s ):' %(a['name'], ','.join( self.inherits )) )
			else:
				py.append( 'class %s( object ):' %a['name'] )

			py.append( '	"""' )
			py.append( '	classpath : %s' %self.classpath )
			strs = []; bools = []
			for key in self.info:
				if key == 'doxygen': continue
				val = self.info[key]
				if type(val) is str:
					strs.append( '	%s : %s' %(key,val) )
				elif type(val) is bool:
					bools.append( '	%s : %s' %(key,val) )
			py += strs; py += bools

			if 'template_info' in self.info:
				py.append( '	template info : %s' %self.info['template_info'] )


			if 'doxygen' in self.info and self.info['doxygen']:
				py.append( '\t'+('~'*80) )
				dox = self.info['doxygen'].replace('/*','').replace('*/','')
				py.append( '\t\n'.join( dox.splitlines() ) )
			py.append( '	"""' )

			if self.namespace:
				py.append( '%s = %s' %(a['classpath'].replace('::','.'), a['name']) )

			py.append( '' )

		return '\n'.join(cpp), '\n'.join(py)


def build_wrapper_property( prop, cpp, _py_properties, path ):
	cpp.append( '//'+prop.basicinfo )
	if 'std' in prop.info: return

	_prop_ = []
	_py_properties[ prop.name ] = _prop_

	nfo = { 
		'property': 'get',
		'property_name': prop.name,
		'name': '%s_%s__GETPROPERTY__' %(path.replace('::','_'), prop.name),
		'parameters':[],
		'returns': prop.info['raw_type'],
		'returns_pointer': prop.info['pointer'],
		'returns_fundamental': prop.info['fundamental'],
		'returns_ctypes': prop.info['ctypes_type'],
		'returns_array' : 0,
	}
	_prop_.append( nfo )

	if 'enum' in prop.info:
		nfo['returns'] = 'int'
	if 'array' in prop.info:
		nfo['returns_array'] = prop.info['array']


	if nfo['returns_fundamental']:
		t = nfo['returns']
		if nfo['returns_pointer']:
			t += '*' * nfo['returns_pointer']
		elif nfo['returns_array']:
			#t += '[%s]' %nfo['returns_array']
			t += '*'
		getter = '%s  %s( void* object )' %(t, nfo['name'])
		getter += ' { return ((%s*)object)->%s; }'	%(path,prop.name)

	else:
		getter = 'void*  %s( void* object )' %nfo['name']
		getter += ' { return (void*)((%s*)object)->%s; }'	%(path,prop.name)

	cpp.append( getter )

	############### setter ###############
	if prop.info['constant']: return	# can not set a const

	narg = {}
	for key in 'name type raw_type pointer fundamental ctypes_type enum'.split():
		if key in prop.info: narg[ key ] = prop.info[ key ]
	nfo = { 
		'property': 'set',
		'property_name': prop.name,
		'name': '%s_%s__SETPROPERTY__' %(path.replace('::','_'), prop.name),
		'parameters':[ narg ],
		'returns': 'void',
		'returns_fundamental': True,
		'returns_ctypes': 'ctypes.c_void_p',
	}
	_prop_.append( nfo )

	setter = 'void %(name)s( void* object,' %nfo
	if prop.ctype != prop.cpptype:
		setter += 'void* arg'
		if prop.info['reference']:
			arg = '(%s&)arg' %prop.cpptype
		elif prop.info['pointer'] or 'array' in prop.info:
			arg = '(%s*)arg' %prop.cpptype
		else:
			arg = '(%s&)arg' %prop.cpptype
	else:
		ptrs = '*' * prop.info['pointer']
		#if not ptrs and 'array' in prop.info: ptrs = '*'

		if prop.info['constant'] and prop.info['reference']:
			setter += 'const %s & arg' %prop.ctype
		elif prop.info['constant']:
			setter += 'const %s %s arg' %(prop.ctype,ptrs)
		elif prop.info['reference']:
			setter += '%s & arg' %prop.ctype
		else:
			setter += '%s %s arg' %(prop.ctype,ptrs)

		if 'enum' in prop.info:	# int must be cast to enum
			arg = '(%s%s)arg' %(prop.info['enum'], ptrs)
		else:
			arg = 'arg'		# pass directly

	if 'array' in prop.info:	# array size comes after var name
		setter = setter + '[%s]' %prop.info['array']
		## no copy assignment of array's in C ##
		#http://stackoverflow.com/questions/6165235/i-get-incompatible-types-in-the-assignment
		setter += ' ) { '
		for i in range( prop.info['array'] ):
			setter += '\n\t((%s*)object)->%s[ %s ] = %s[ %s ];'	%(path, prop.name, i, arg, i)
		setter += ' } '

	else: setter += ' ) { ((%s*)object)->%s = %s; }'	%(path,prop.name, arg)

	cpp.append( setter )


def build_wrapper_constructor( i, func, cpp, _py_constructors, path=None, name=None, template=None ):
	assert path
	if not name: name = path
	Tclass = None
	if '<' in path: Tclass = path.split('<')[0]

	ok = True
	_c_ = []; _cpp_ = []; _defaults_ = 0
	for j, arg in enumerate(func.arguments):
		if 'default' in arg.info: _defaults_ += 1

		ctype = arg.info['raw_type']

		if template and 'template' in arg.info and 'template_class' not in arg.info:	# no copy constructors
			ctype = template['typename']
			arg.info['raw_type'] = ctype
			arg.info['ctypes_type'] = template['ctypes_type']
			arg.info['fundamental'] = True

		if not arg.info['fundamental']:
			ok = False

		ptrs = '*' * arg.info['pointer']
		_c_.append( '%s %s arg%s' %(ctype, ptrs, j) )
		if 'enum' in arg.info:
			a = arg.info['enum']
			if Tclass and a.startswith(Tclass): a = a.replace(Tclass, path)
			_cpp_.append( '(%s&)arg%s' %(a,j) )
		else:
			_cpp_.append( 'arg%s' %j )

	cpp.append( '/* constructor: ' )
	cpp.append( func.info['debug'] )
	for arg in func.arguments: cpp.append( arg.basicinfo )
	cpp.append( '*/' )
	safename = name.replace('::','_')
	n = '%s__new__' %safename
	if i != 0: n += str(i)
	if not ok and _defaults_ == len(func.arguments):	# if all args have default we can call
		cpp.append( 'void* %s() { return (void*)(new %s()); } ' %(n, path) )
		############# PYTHON ##############
		nfo = {'name':n, 'parameters':[]}
		_py_constructors.append( nfo )
		###################################
	elif ok:
		if i != 0: n += str(i)
		cpp.append( 'void* %s( %s ) { return (void*)(new %s( %s )); } ' %(n, ','.join(_c_), path, ','.join(_cpp_)) )
		if _defaults_ and _defaults_ == len(func.arguments):
			cpp.append( 'void* %s__defaults__() { return (void*)(new %s()); } ' %(n, path) )
			############# PYTHON ##############
			nfo = {'name':n+'__defaults__', 'parameters':[]}
			_py_constructors.append( nfo )
			###################################

		############# PYTHON ##############
		nfo = {'name':n, 'parameters':[]}
		_py_constructors.append( nfo )
		for arg in func.arguments:
			narg = {}
			for key in 'name type raw_type pointer fundamental ctypes_type enum default'.split():
				if key in arg.info: narg[ key ] = arg.info[ key ]
			nfo['parameters'].append( narg )
		###################################


def build_wrapper_function( meth, gname, cpp, _py_methods, path=None, template=None ):
	print( type(meth), gname, type(cpp), type(_py_methods), path )
	#assert path
	m = { 'func': meth.name, 'signature':gname, 'classpath':path }


	if 'doxygen' in meth.info and meth.info['doxygen']:
		dox = meth.info['doxygen'].replace('/*','').replace('*/','')
		cpp.append( '/*%s*/' %dox )

	cpp.append( '/*' + meth.debug )
	for key in meth.info:
		if key.startswith('returns'): cpp.append( '	%s: %s' %(key,meth.info[key]) )
	cpp.append( '*/')

	#m['func'] = meth.name
	#m['signature'] = gname

	if meth.info['returns_fundamental']:
		if 'returns_const' in meth.info: r = 'const '
		else: r = ''
		r += meth.returns
		if meth.info['returns_pointer']: r += '*' * meth.info['returns_pointer']
		m['returns'] = r

	elif meth.info['returns_class']:
		if 'returns_const' in meth.info: m['returns'] = 'const void*'
		else: m['returns'] = 'void*'

	else:
		print( 'WARN: returns opaque unknown', meth.name, meth.returns )
		if 'returns_const' in meth.info: m['returns'] = 'const void*'
		else: m['returns'] = 'void*'


	if meth.info['static']: cargs = []
	else: cargs	= ['void* object']		# args with C linkage
	cppargs	= [ ]				# args passed to C++ method
	opargs	= [ ]

	for idx,arg in enumerate(meth.arguments):

		cppargs.append( '//'+arg.basicinfo )

		if arg.ctype != arg.cpptype:
			cpptype = arg.cpptype
			if 'template' in arg.info and template:
				if 'template_class' in arg.info:
					cpptype = cpptype.replace('<%s>'%arg.info['template'], '<%s>'%template['typename'])
					arg.info['ctypes_type'] = 'ctypes.c_void_p'

				elif 'ctypes_type' in template:
					cpptype = template['typename']
					arg.info['ctypes_type'] = template['ctypes_type']
					arg.info['fundamental'] = True
				else:
					print( 'TODO' ); arg.info

			elif template: print( 'WARN-TODO', arg )

			if arg.info['pointer'] >= 2:
				ptrs = '*' * arg.info['pointer']
				cargs.append( 'void%s arg%s' %(ptrs,idx) )
			else:
				cargs.append( 'void* arg%s' %idx )	# incoming object as void*

			if arg.info['reference']:
				if arg.info['pointer']:
					ptrs = '*' * arg.info['pointer']
					cppargs.append( '(%s%s &)arg%s' %(cpptype,ptrs,idx) )
				elif arg.info['constant']:
					cppargs.append( '(const %s&)arg%s' %(cpptype,idx) )
				else:
					cppargs.append( '(%s&)arg%s' %(cpptype,idx) )

			elif arg.info['pointer']:
				ptrs = '*' * arg.info['pointer']
				if arg.info['constant']: cppargs.append( '(const %s%s)arg%s' %(cpptype,ptrs,idx) )
				else: cppargs.append( '(%s%s)arg%s' %(cpptype,ptrs,idx) )

			elif 'array' in arg.info:
				cppargs.append( '(%s*)arg%s' %(cpptype,idx) )
			else:
				cppargs.append( '(%s&)arg%s' %(cpptype,idx) )

		else:
			ptrs = '*' * arg.info['pointer']
			if arg.info['constant'] and arg.info['reference']:
				cargs.append( 'const %s & arg%s' %(arg.ctype,idx) )
			elif arg.info['constant']:
				cargs.append( 'const %s %s arg%s' %(arg.ctype,ptrs,idx) )
			elif arg.info['reference']:
				cargs.append( '%s & arg%s' %(arg.ctype,idx) )
			else:
				cargs.append( '%s %s arg%s' %(arg.ctype,ptrs,idx) )

			if 'enum' in arg.info:	# int must be cast to enum
				#if arg.info['type'] != arg.info['enum'] and '::' in arg.info['type'] and False:
				#	print( 'THIS IS A BUG', arg.info )	# Tundra - July7
				#	cppargs.append( '(%s%s)arg%s' %(arg.info['type'], ptrs, idx) )
				#else:
				if arg.info['reference']: cppargs.append( '(%s%s &)arg%s' %(arg.info['enum'], ptrs, idx) )
				else: cppargs.append( '(%s%s)arg%s' %(arg.info['enum'], ptrs, idx) )

			else: cppargs.append( 'arg%s' %idx )	# pass directly

		if 'array' in arg.info:	# array size comes after var name
			cargs[-1] += '[%s]' %arg.info['array']

		if meth.info['operator']: opargs.append( cppargs[-1] )

	######################################
	a = '%(returns)s  %(signature)s' %m
	a += '( %s )  {' %(', '.join(cargs))
	cpp.append( a )

	if meth.info['operator']:
		print( meth.info )
		b = '((%(classpath)s&)object)' %m
		op = meth.info['operator']
		if op == '[]':
			b += '[ %s ]' %opargs[0]
		else:
			b += op
			b += '(%s)' %opargs[0]
		b = '(%s)' %b

	elif meth.info['static']:	# or free function
		if m['classpath']:
			b = '%(classpath)s::%(func)s' %m
		else:
			b = '%(func)s' %m

		if cppargs: b += '(\n\t\t%s )' %(',\n\t\t'.join(cppargs))
		else: b += '()'

	else:
		b = '((%(classpath)s*)object)->%(func)s' %m
		if cppargs: b += '(\n\t\t%s )' %(',\n\t\t'.join(cppargs))
		else: b += '()'

	if m['returns'] != 'void':
		if 'returns_const' in meth.info: const = 'const '
		else: const = ''
		if m['returns'] in ('void*', 'const void*'):
			if meth.info['returns_pointer']:
				b = 'return (%s void*)( %s )' %(const,b)
			else:
				b = 'return (void*)(& %s )' %b
		else: b = 'return %s' %b
	cpp.append( '\t'+b+';' )
	cpp.append( '}' )

	############# PYTHON ##############
	if meth.name not in _py_methods:
		_py_methods[ meth.name ] = { 'name':meth.name, 'static':meth.info['static'], 'functions': [] }
		for key in meth.info:
			if key.startswith('returns'):
				_py_methods[ meth.name ][ key ] = meth.info[key]

	nfo = {'name':gname, 'parameters':[]}
	_py_methods[ meth.name ][ 'functions' ].append( nfo )
	for arg in meth.arguments:
		narg = {}
		for key in 'name type raw_type pointer fundamental ctypes_type enum default'.split():
			if key in arg.info: narg[ key ] = arg.info[ key ]
		nfo['parameters'].append( narg )
	###################################


#################################################################


class CPlusPlus( SourceCode ):
	UNSAFE_BOOST = (
		'boost::posix_time::time_duration',
		'boost::mutex',
		'boost::timed_mutex',
		'boost::thread',
		'boost::recursive_mutex',
		'boost::recursive_timed_mutex',
		'boost::gregorian',
		'boost::gregorian::date',
		'boost::gregorian::greg_weekday',
		'boost::gregorian::greg_month',
		'boost::detail::interruption_checker',
		'boost::condition_variable',
		'boost::program_options::variable_value',
		'boost::function_base',
		'boost::system::error_code',
		'boost::system',
		'boost::system::error_condition',
		'Poco::MutexImpl',
		'Poco::Exception',
	)

	typedefs = {}	# deprecate? what is using this?

	def add_library( self, name, dynamic=True ):
		if dynamic: self._dynamic_libs.append( name )
		else: self._static_libs.append( name )
	def add_ignore_class( self, name ):
		print( 'ignore class:', name )
		self._ignore_classes.append( name )
	def add_ignore_function( self, name ):
		print( 'ignore function:', name )
		self._ignore_functions.append( name )


	def parse( self, string, debug=False ):
		os.system( 'rm /tmp/_compile_.*' )
		self._dynamic_libs = []
		self._static_libs = []
		self._ignore_classes = []
		self._ignore_functions = []
		self.classes = []
		self.functions = []

		if '--no-preprocessor' in sys.argv: H = CppHeaderParser.CppHeader( self.source_url )
		else: H = CppHeaderParser.CppHeader( string, argType='string' )
		CPlusPlus.typedefs = H.typedefs
		self._parse_tree = H

		print( '---------------- global namespaces ----------------' )
		print( H.namespaces )
		print( '---------------- global typedefs ----------------' )
		print( H.typedefs )
		#print( '---------------- global enums ----------------' )
		#print( H.global_enums )
		#print( '---------------- classes ----------------' )
		#print( H.classes.keys() )
		#print( '---------------- structs ----------------' )
		#print( H.structs.keys() )



	def save(self, name):
		self._wrapper_name = name
		self.shared_library_name = name + '-CAPI'
		genpy = [
			CTYPES_HEADER,
			'_clib_name_ = "%s"   #generated C wrapper#' %self.shared_library_name,
		]

		H = self._parse_tree
		for k in H.classes_order:
			if k['namespace'] and k['namespace']+'::'+k['name'] in self.UNSAFE_BOOST:
				print( 'skipping unsafe class', k['name'] )
			elif k['name'] in self._ignore_classes:
				print( 'user - skipping unsafe class', k['name'] )

			else:
				self.classes.append( CPP_class(k) )

		for info in H.functions:
			f = CPP_method( info )
			#if not f.valid: continue
			self.functions.append( f )

		H.namespaces.sort()
		for ns in H.namespaces:
			classname = '__namespace__' + ns.replace('::','_'); name = ns.split('::')[-1]
			genpy.append( 'class %s(object): pass' %classname )
			genpy.append( '%s = %s()' %(name, classname) )
			if '::' in ns:
				genpy.append( '%s = %s' %(ns.replace('::','.'), name) )
		genpy.append( '#'*80 )

		cppfile = '/tmp/%s_wrapper.cpp' %self.source_file
		gencpp = []
		gencpp.append( '/* generated by: RPythonic %s */' %VERSION )
		if self.source_file.endswith('.h') or self.source_file.endswith('.hpp'):
			gencpp.append( '#include "%s" ' %self.source_file )
		for h in INSERT_HEADERS:
			if h.endswith('.h') or h.endswith('.hpp'):
				gencpp.append( '#include "%s" ' %os.path.split(h)[-1] )
		gencpp.append( '#include "stdint.h" ' )

		#if '#include <map>' no in gencpp:		# need to make a custom wrapper for std::map and std::string
		#	gencpp.append( '#include <map>' )

		# typedef a struct for each object type, holds a single field 'object' #
		#for c in self.classes: gencpp.append( 'typedef struct {%s object; } %sOBJECT;' %(c.name,c.name) )
		#for c in self.classes: gencpp.append( 'typedef struct %sOBJECT;' %c.name )

		gen_func_names = {}		# name : 0 # avoids name collisions
		gencpp.append( 'extern "C" {' )
		for c in self.classes:
			#if c.classpath.startswith('std::') or c.classpath.startswith('__cxxabiv1::'):
			if c.classpath.startswith('std::') or c.classpath.startswith('__cxxabiv1::') or c.classpath.startswith('boost::'):
				gencpp.append( '/* SKIPPING: %s */' %c.classpath )
				continue
			if c.invalid:
				gencpp.append( '/* SKIPPING INVALID CLASS: %s */' %c.classpath )
				continue
			if c.unsafe_template:
				gencpp.append( '/* SKIPPING UNSAFE TEMPLATE CLASS: %s */' %c.classpath )
				continue

			if 'internal' in c.info:
				gencpp.append( '/* SKIPPING PROTECTED/PRIVATE INTERNAL CLASS: %s */' %c.classpath )
				continue

			if c.info['parent'] and c.info['parent'] in self._ignore_classes:
				gencpp.append( '/* SKIPPING NESTED CLASS OF IGNORED: %s */' %c.classpath )
				continue

			#if c.name == 'SceneNode': c.info.show_all(); assert 0


			cpp, py = c.gen_wrapper( gen_func_names )
			gencpp.append( cpp )
			genpy.append( py )

		gencpp.append( '/'*100 )
		gencpp.append( '/////////////////// FUNCTIONS ///////////////////////' )
		genpy.append( '#'*100 )
		genpy.append( '#/////////////////// FUNCTIONS ///////////////////////#' )

		STDC = 'funlockfile ftrylockfile flockfile pclose fileno_unlocked fileno ferror_unlocked feof_unlocked clearerr_unlocked ferror feof clearerr fgetpos64 ftello64 fseeko64 fgetpos ftello fseeko rewind ftell fseek ungetc putw getw putc_unlocked fputc_unlocked fputc fgetc_unlocked getc_unlocked fgetc setlinebuf open_memstream fflush_unlocked fflush fclose tmpfile64 tmpfile __overflow __uflow'.split()
		done = []
		for f in self.functions:
			if f.namespace.startswith('std'): continue
			elif f.namespace.startswith('__gnu_cxx'): continue
			elif f.namespace.startswith('boost'): continue
			elif f.namespace.startswith('boost_optional_detail'): continue

			elif not f.namespace and f.name in STDC:
				continue
			elif not f.namespace and f.name.startswith('_IO_'):
				continue
			elif not f.namespace and f.name.startswith('_'):	# good rule - ignore "_somefunction" ?
				continue
			elif not f.namespace:	# good rule - ignore free function not in a namespace ?
				continue
			elif 'unresolved_parameters' in f.info:
				gencpp.append( '/* %s.%s <UNRESOLVED PARAMETERS> */' %(f.namespace, f.name) )
				continue

			elif f.namespace+'::'+f.name in self._ignore_functions: continue

			if not f.namespace: gname = 'toplevel_' + f.name
			else: gname = '%s_%s' %(f.namespace.replace('::','_'), f.name)
			if gname in done:
				gencpp.append( '/* %s.%s <DUPLICATE> */' %(f.namespace, f.name) )
				continue
			else: done.append( gname )

			info = {}
			f.info['static'] = True
			build_wrapper_function( f, gname, gencpp, info, path=f.namespace )

			## ugly
			info = info[ f.name ]['functions'][0]
			for key in f.info:
				if key.startswith('returns'):
					info[ key ] = f.info[key]
			## end ugly

			p = '%s = meta.function( %s )' %(f.name, info )
			genpy.append( p )
			p = '%s = %s' %(f.namespace.replace('::','.')+f.name, f.name )
			genpy.append( p )
		assert self.functions

		gencpp.append( '} /* end extern "C" */' )


		genpy.append( '#'*100 )
		genpy.append( '#/////////////////// ENUMS ///////////////////////#' )
		for enum in H.enums:
			if 'name' in enum:
				genpy.append( '%s = {' %enum['name'] )
				for d in enum['values']:
					try: int( d['value'] )
					except: d['value'] = 0
					genpy.append( '	"%(name)s" : %(value)s,' %d )
				genpy.append( '}' )


		self._cpp_wrapper_source = '\n'.join( gencpp )
		self._py_wrapper_source = '\n'.join( genpy )

		################## finalize #####################
		f = open( '/tmp/_debug_wrapper.cpp', 'wb' )
		f.write( self._cpp_wrapper_source ); f.close()
		f = open( '/tmp/_debug_wrapper.py', 'wb' )
		f.write( self._py_wrapper_source ); f.close()

		url = os.path.join( CACHEDIR, CTYPES_OUTPUT )
		f = open(url, 'wb' )
		f.write( self._py_wrapper_source ); f.close()

		# what is runpath? http://blogs.oracle.com/ali/entry/changing_elf_runpaths
		if self._dynamic_libs: runpath = '/usr/local/lib'
		else: runpath = None

		lib = GCC.compile( 
			self.shared_library_name, 
			source=self._cpp_wrapper_source, 
			includes=self.includes, 
			defines=MACRO_DEFS, 
			cplusplus=True,
			warnings=False,
			links = self._static_libs + self._dynamic_libs,
			runpath = runpath,
		)
		if not lib: assert 0


	def gen_rpython(self):	return self.gen_python()
	def gen_python(self): return '<todo>'
	def gen_rffi(self): return '<todo>'
	def gen_ctypes(self): return self._gen_ctypes



PYPY_HEADER = '''
## generated by RPythonic %s
## http://code.google.com/p/rpythonic/
'''%VERSION

PYPY_HEADER += '''
from pypy.rpython.lltypesystem import lltype, rffi
from pypy.rpython.tool import rffi_platform as platform
from pypy.translator.tool.cbuild import ExternalCompilationInfo
from pypy.rpython.annlowlevel import llhelper		# for rpy callbacks from C

def _def_cstruct( struct, *fields ):		# from lltype Struct __init__ (line 186)
	flds = {}; names = []; struct._arrayfld = None
	for name, typ in fields:
		name = 'c_'+name
		names.append(name)
		if name in flds: raise TypeError("repeated field name")
		flds[name] = typ
	if fields:
		for name, typ in fields[:-1]: typ._inline_is_varsize(False)
		name, typ = fields[-1]
		if typ._inline_is_varsize(True): struct._arrayfld = name
	struct._flds = lltype.frozendict(flds)
	struct._names = tuple(names)

def _c_external_function(name, args=[], result=lltype.Void):	
	if len(args)==1 and args[0]==lltype.Void: args = []	# if function takes void then args=[], not args=[lltype.Void]
	func = rffi.llexternal(name, args, result, compilation_info=_ECI_)
	gen = False
	for arg in args:
		if arg is rffi.FLOAT: gen = True; break	# FLOAT (single) is the issue because 1.0 in python is a double
	if not gen: return func		# use directly
	else:	# generate wrapper
		r=rffi	# scope issue - pypy bug?
		head = ''; body = ''
		for i,arg in enumerate(args):
			head += 'a%s,' %i
			if arg is rffi.FLOAT: body += 'r.cast( r.FLOAT, a%s ),' %i
			else: body += 'a%s,' %i
		gen = 'lambda %s: func(%s)' %(head,body)
		print('wrapper func: %s: %s' %(name,gen))
		return eval( gen, locals() )
'''



test = None
class C( SourceCode ):		# using pycparser

	def parse(self, string, debug=0 ):	# called by init
		self.set_ctypes_header()
		self.set_rffi_header()

		#CParser = pycparser.c_parser.CParser( lex_optimize=False, yacc_debug=True, yacc_optimize=False )
		parser = CParser( lex_optimize=False, yacc_debug=True, yacc_optimize=False )
		if '--debug' in sys.argv:
			ast = parser.parse( string, debuglevel=2 )
		else:
			ast = parser.parse( string, debuglevel=debug )
		self.ast = ast
		self.python = self.output = []
		self.structs = []
		self.unions = []
		self.funcs = []
		self.funcdefs = []
		self.typedefs = []
		self.somethings = []
		self.indent = 0
		self.objects = []
		self.walk( ast )
		self.cyclics = Union.mark_cyclic()

	def set_ctypes_header(self):
		self.CTYPES_HEADER =  CTYPES_HEADER + '\n' + '''
_clib_name_ = '%s'
print('loading lib', _clib_name_)
print( os.path.abspath( os.path.curdir ) )
_ctypes_lib_ = _load_ctypes_lib( _clib_name_ )
assert _ctypes_lib_
print( _ctypes_lib_._name )
''' %self.shared_library_name


	def set_rffi_header( self ):
		cfg = {
			'header' : [ self.source_url ],
			'paths' : list(self.includes),
			'lib': LIBS	#[ self.shared_library_name ],
		}

		g = []
		if self.platform == 'android':		# for android we ignore pypy build system #
			g.append( '_ECI_ = ExternalCompilationInfo()' )
		else:						# use pypy build system to generate standalone executables #
			g.append( '_ECI_ = ExternalCompilationInfo( includes = %(header)s, include_dirs = %(paths)s, link_files = %(lib)s )' %cfg )

		g.append( 'try: platform.verify_eci(_ECI_)' )
		g.append( 'except: print( "can not verfiy ECI - this is ok if the target is Android" )' )

		self.RFFI_HEADER = PYPY_HEADER + '\n' + '\n'.join( g )

	def gen_rpython(self):	return self.gen_python()
	def gen_python(self):
		indent = []; s = ''
		for o in self.objects:
			for line in o.gen_python():
				s += ''.join( indent ) + line + '\n'
		return TRANS_HEADER + '\n' + s

	def generate_rffi_wrapper(self):
		a = '################ Globals ##############\n'
		for name in self.macro_globals:
			value = self.macro_globals_values[ name ]
			if type(value) is str: a += '%s = "%s"\n' %(name,value)
			else: a += '%s = %s\n' %(name,value)
		a += '################# ENUMS ################\n'
		a += '\n'.join( [o.gen_rffi() for o in SomeThing.get_enums()] ) + '\n'
		us = SomeThing.get_unions_and_structs()

		a += '################# Forwardref Structs ################\n'
		a += '\n'.join( [o.gen_rffi(declare=False, forward=True) for o in us] ) + '\n'
		a += '################# Declare Structs ################\n'
		a += '\n'.join( [o.gen_rffi(declare=True) for o in us] ) + '\n'
		a += '################# Define Structs ################\n'
		a += '\n'.join( [o.gen_rffi(declare=False) for o in us] ) + '\n'
		a += '################# Become Structs ################\n'
		a += '\n'.join( [o.gen_rffi(declare=False, become=True) for o in us] ) + '\n'
		a += '################# Functions ################\n'
		a += '\n'.join( [o.gen_rffi() for o in SomeThing.get_funcs()] )
		return self.RFFI_HEADER + a

	def generate_ctypes_wrapper(self):	
		a = '## macro globals ##\n'
		for name in self.macro_globals:
			value = self.macro_globals_values[ name ]
			if type(value) is str: a += '%s = "%s"\n' %(name,value)
			else: a += '%s = %s\n' %(name,value)

		a += '## enums ##\n'
		a += '\n'.join( [o.gen_ctypes() for o in SomeThing.get_enums()] ) + '\n'
		a += '## simple enums ##\n'
		a += 'RPYTHONIC_GLOBAL_ENUMS = { \n'
		for o in SomeThing.get_enums():
			if not o.is_named_enum():
				a += o.dict_format() + '\n'
		a += '\n}\n'

		## declare unions/structs first, so that they can self-reference
		u = SomeThing.get_unions_and_structs()
		a += '\n'.join( [o.gen_ctypes(declare=True) for o in u] ) + '\n'

		## func prototypes ##
		#for o in SomeThing.get_funcs(): a += '%s\n' %o.gen_ctypes(prototype=True)
		protos = []
		for o in u:
			for f in o.functions():
				#n = f.name()
				n = f.type()
				if n.startswith('function:'):
					n = n.split(':')[-1]
					if n not in protos: protos.append( n )
		## check for functions that take arguments pointing to another function(callback)
		for func in SomeThing.get_funcs():
			for arg in func.args:
				argtype = arg.type()
				if argtype.startswith('function:'):
					n = argtype.split(':')[-1]
					if n not in protos: protos.append( n )

		## no need for messy func prototypes - may18th ##
		#a += '## callback prototypes ##\n'
		#for n in protos:
		#	if n in SomeThing.Functions:
		#		func = SomeThing.Functions[ n ]
		#		a += '%s\n' %func.gen_ctypes(prototype=True)
		#	else: assert 0

		a += '## union and structures ##\n'
		## define unions/structs members (must be ordered), cyclic fields can still break ctypes - is that possible in C?
		u = SomeThing.get_unions_and_structs( sort=True )
		a += '\n'.join( [o.gen_ctypes(declare=False) for o in u] ) + '\n'

		a += '## wrapper functions ##\n'
		## write wrapper functions
		for o in SomeThing.get_funcs():
			if not o.name().startswith('__') and not o.static:
				a += '%s\n' %o.gen_ctypes()

		_tail = [
				'_rpythonic_convert_structs_to_objects()',
				CTYPES_FOOTER,	# this must come after structs are converted to smart-objects
				'_rpythonic_setup_return_wrappers()',
				'_rpythonic_make_nice_global_enums_()',
				'_rpythonic_clean_up_missing_functions_()',
				RAYMOND_HETTINGER,
				#'hettinger_transform()',	# can be slow for big ones like gtk
		]
		return self.CTYPES_HEADER + '\n' + a + '\n' + '\n'.join(_tail)



	def get_func_names( self ): return [ f.name for f in self.funcs ]
	def get_struct_names( self ): return [ s.name for s in self.structs ]


	def show( self ):
		for line in self.python: print( line )

	def walk( self, node ):
		#if isclass( node, 'Struct' ):
		#	self.structs.append( Struct(node) ); print 'this happens? struct without declare'; raise SyntaxError
		#elif isclass( node, 'Union' ):
		#	self.unions.append( Union(node) ); print 'this happens? union without declare'; raise SyntaxError
		if isclass( node, 'FuncDef', 'FuncDecl' ):
			o = Function(node)
			self.funcs.append( o )
			self.objects.append( o )
		elif isclass( node, 'Decl', 'TypeDecl', 'PtrDecl', 'Typedef' ):
			o = SomeThing(node)
			self.somethings.append( o )
			self.objects.append( o )
		else:
			print( 'skipping', node )
			children = node.children()
			if children:
				for child in children:
					self.walk( child )


if pycparser:
	class CParser( pycparser.c_parser.CParser ):
		#def p_pp_directive(self,p):
		#	""" pp_directive  : PPHASH 
		#	"""
		#	print( 'FIXME on new pycparser update' )	#example: #pragma pack (push, 1)

		def p_decl_body(self,p):
			""" decl_body : declaration_specifiers init_declarator_list_opt
			"""
			#pycparser.c_parser.CParser.p_decl_body(self,p)
			try: pycparser.c_parser.CParser.p_decl_body(self,p)
			except:
				spec = p[1]
				print('_'*80)
				print(spec)
				for t in spec['type']: print('decl body', t )
				raise SyntaxError

		def p_error(self, p):
			#print( dir(self.cparser))
			#print( 'state stack:')

			#print( self.cparser.symstack )
			#print( dir(self.clex) )
			#print( self.clex.tokens )
			if '--skip-parser-errors' in sys.argv:
				if p:
					pprint( 'pycparser error, before: %s' %p.value, 1 )
					pprint( p.lineno, 1 )
				else: pprint( 'error at end of file!' )
			else:
				#print( self.cparser.productions )
				#print( self.cparser.statestack )
				#for state in self.cparser.statestack: print( '\t%s' %self.cparser.productions[state] )
				print(p)
				if p: self._parse_error( 'before: %s' % p.value, self._coord(p.lineno))
				else: self._parse_error('At end of input', '')

		# Due to the order in which declarators are constructed,
		# they have to be fixed in order to look like a normal AST.
		# 
		# When a declaration arrives from syntax construction, it has
		# these problems:
		# * The innermost TypeDecl has no type (because the basic
		#   type is only known at the uppermost declaration level)
		# * The declaration has no variable name, since that is saved
		#   in the innermost TypeDecl
		# * The typename of the declaration is a list of type 
		#   specifiers, and not a node. Here, basic identifier types
		#   should be separated from more complex types like enums
		#   and structs.
		#
		# This method fixes these problem.
		#
		def _fix_decl_name_type(self, decl, typename):
			""" Fixes a declaration. Modifies decl.
			"""
			c_ast = pycparser.c_ast
			# Reach the underlying basic type
			#
			type = decl
			while not isinstance(type, c_ast.TypeDecl): type = type.type

			decl.name = type.declname
			type.quals = decl.quals

			# The typename is a list of types. If any type in this 
			# list isn't a simple string type, it must be the only
			# type in the list (it's illegal to declare "int enum .."
			# If all the types are basic, they're collected in the
			# IdentifierType holder.
			#
			if '__const' in typename: typename.remove('__const')	# this solves some rare cases
			for tn in typename:
				if not isinstance(tn, str):
					if len(typename) > 1:
						if '--skip-parser-errors' in sys.argv:
							pprint( 'WARNING: Invalid multiple types specified: %s' %tn.coord, 1 )
							return decl
						else:
							pprint( typename, 1 )
							self._parse_error("Invalid multiple types specified", tn.coord)
					else:
						type.type = tn
						return decl

			type.type = c_ast.IdentifierType(typename)
			return decl



class AndroidPackage( object ):
	'''
	Requires:
		1. ant	(ubuntu: "apt-get install ant")
		2. Android 2.3.3 (API level 10)
		3. hack pypy source code - see below

	Hack PyPy:
		pypy/translator/c/src/profiling.c
			line3: #if defined(__GNUC__) && defined(__linux__)
			change-to: #if defined(__GNUC__) && defined(__linux__) && !defined(ANDROID)
		pypy/translator/c/src/ll_strod.h
		replaces: lines 43 and 112:
		# ifdef ANDROID
		  decimal_point = ".";
		  decimal_point_len = 1;
		# else
		  locale_data = localeconv();
		  decimal_point = locale_data->decimal_point;
		  decimal_point_len = strlen(decimal_point);
		# endif

	'''

	DROID_HEADER = ''#open( os.path.join(RPYTHONIC_DIR,'android-glue.h'), 'rb' ).read().decode('utf-8')
	DROID_FOOTER = ''#open( os.path.join(RPYTHONIC_DIR,'android-glue.c'), 'rb' ).read().decode('utf-8')

	SDK_ROOT = NDK_ROOT = None
	@classmethod
	def set_sdk_root( self, path ):
		path = os.path.abspath( path )
		assert os.path.isdir( path )
		self.SDK_ROOT = path
	@classmethod
	def set_ndk_root( self, path ):
		path = os.path.abspath( path )
		assert os.path.isdir( path )
		self.NDK_ROOT = path

	def upload( self, emulator=False ):
		adb = os.path.join( self.sdk_platform_tools, 'adb' )
		if emulator: cmd = '%s -e install -r %s' %(adb,self.apk)
		else: cmd = '%s install -r %s' %(adb,self.apk)
		print( cmd )
		os.system( cmd )

	def logcat( self ):	# blocking ctrl+c exits
		adb = os.path.join( self.sdk_platform_tools, 'adb' )
		#cmd = '%s logcat *:E' %adb	# show only warn
		cmd = '%s logcat' %adb	# show only warn
		print( cmd )
		os.system( cmd )


	def __init__(self, headers, sources, name='apptest1', ndk_version='android-9'):
		assert self.SDK_ROOT
		assert self.NDK_ROOT
		self.sdk_tools = os.path.join( self.SDK_ROOT, 'tools' )
		assert os.path.isdir( self.sdk_tools )
		self.sdk_platform_tools = os.path.join( self.SDK_ROOT, 'platform-tools' )
		assert os.path.isdir( self.sdk_platform_tools )


		self.ndk_version = ndk_version
		self.ndk_platform = os.path.join( 
			self.NDK_ROOT, 
			'platforms/%s/arch-arm/' %self.ndk_version
		)
		assert os.path.isdir( self.ndk_platform )
		self.ndk_usr = os.path.join( self.ndk_platform, 'usr')
		self.ndk_include = os.path.join( self.ndk_usr, 'include' )
		self.ndk_lib = os.path.join( self.ndk_usr, 'lib' )
		assert os.path.isdir( self.ndk_usr )
		assert os.path.isdir( self.ndk_include )
		assert os.path.isdir( self.ndk_lib )

		self.pypy_include = os.path.join(PATH2PYPY,'pypy/translator/c/')
		assert os.path.isdir( self.pypy_include )

		from pypy.tool.udir import udir
		print('-'*80); print('#### CREATING NEW ANDROID PROJECT ####')
		## android NDK expects a 'jni' folder under project directory ##
		jnidir = udir.join('jni').ensure(dir=1)	# pypy udir automatically gives us a temp dir
		projectdir = os.path.split( jnidir.strpath )[0]
		self.new_project( projectdir, name=name )
		print( projectdir )
		print('-'*80)
		## make things simple, copy everything to jnidir ##
		for h in headers:
			h.copy( jnidir )
			#n = jnidir.join( h.basename )
			#n.write( ANDROID_HEADER_HACK + '\n' + h.read() )
		for f in sources:
			if f.basename.startswith('module'):
				data = f.read()
				data = data.replace( '\nextern int errno;\n', '\n\n' )	# ugly hack
				n = jnidir.join( f.basename )
				n.write( data )
			#elif f.basename.startswith('testing_'):	# target
			elif f.basename == 'implement.c':
				data = self.DROID_HEADER + '\n' + f.read() + '\n' + self.DROID_FOOTER
				n = jnidir.join( f.basename )
				n.write( data )
			else: f.copy( jnidir )


		mk = self.new_android_makefile(sources, path=jnidir)

		ndkbuild = os.path.join(self.NDK_ROOT,'ndk-build')
		assert os.path.isfile( ndkbuild )
		print('-'*80); print('#### %s ####'%ndkbuild); print('-'*80)
		os.system( 'cd %s; %s -B V=1' %(projectdir, ndkbuild))
		print('-'*80)

		os.system( 'cd %s; ant debug' %projectdir )

		print( projectdir )
		self.apk = os.path.join( projectdir, 'bin/%s-debug.apk'%name )
		assert os.path.isfile( self.apk )

		#tar = tarfile.open(name='/tmp/my_android_project.tar.gz', mode = 'w:gz')
		#tar.add(projectdir)
		#tar.close()


	def new_project( self, path, name='my_android_app', activity='rpytest4', package='com.rpython.tests' ):
		cmd = '%s create project ' %os.path.join( self.sdk_tools, 'android' )
		cmd += ' --target "android-10" --name %s' %name
		cmd += ' --path %s' %path
		cmd += ' --activity %s' %activity
		cmd += ' --package %s' %package
		os.system(cmd)
		## override AndroidManifest so that android:hasCode="false" ##
		manifest = '''<?xml version="1.0" encoding="utf-8"?>
			<!-- BEGIN_INCLUDE(manifest) -->
			<manifest xmlns:android="http://schemas.android.com/apk/res/android"
				package="com.rpythonic.test"
				android:versionCode="1"
				android:versionName="1.0">
			    <!-- This is the platform API where NativeActivity was introduced. -->
			    <uses-sdk android:minSdkVersion="9" />

			    <!-- This .apk has no Java code itself, so set hasCode to false. -->
			    <application android:label="@string/app_name" android:hasCode="false">

				<!-- Our activity is the built-in NativeActivity framework class.
				     This will take care of integrating with our NDK code. -->
				<activity android:name="android.app.NativeActivity"
					android:label="@string/app_name"
					android:configChanges="orientation|keyboardHidden">
				    <!-- Tell NativeActivity the name of or .so -->
				    <meta-data android:name="android.app.lib_name"
					    android:value="implement" />
				    <intent-filter>
					<action android:name="android.intent.action.MAIN" />
					<category android:name="android.intent.category.LAUNCHER" />
				    </intent-filter>
				</activity>
			    </application>

			</manifest>'''
		f = open( os.path.join(path,'AndroidManifest.xml'), 'wb' )
		f.write( manifest )
		f.close()

	def new_android_makefile(self, cfiles, path=None, exe_name=None, shared=False):
		shared = []
		static = []
		for c in cfiles:
			#if c.basename.startswith('testing'): shared.append( c )
			if c.basename == 'implement.c': shared.append( c )
			else: static.append( c )

		appmk = path.join( 'Application.mk' )
		appmk.write( 'APP_ABI := armeabi armeabi-v7a\nAPP_PLATFORM := android-9\n' )

		mk = path.join( 'Android.mk' )
		g = 'LOCAL_PATH := $(call my-dir)\n'	# note no whitespace after $(call my-dir) ## make: *** No rule to make target

		staticnames = 'android_native_app_glue'
		for c in static:
			g += 'include $(CLEAR_VARS) \n'
			g += 'LOCAL_CFLAGS := -I%s -DLINKER_DEBUG=1\n' %self.pypy_include
			g += 'LOCAL_MODULE := %s \n' %c.purebasename
			g += 'LOCAL_SRC_FILES := %s \n' %c.basename
			g += 'LOCAL_PRELINK_MODULE := false\n'
			g += 'include $(BUILD_STATIC_LIBRARY) \n'
			staticnames += ' '+c.purebasename

		for c in shared:
			g += 'include $(CLEAR_VARS) \n'
			g += 'LOCAL_CFLAGS := -I%s -DLINKER_DEBUG=1\n' %self.pypy_include
			g += 'LOCAL_MODULE := %s \n' %c.purebasename
			g += 'LOCAL_SRC_FILES := %s \n' %c.basename
			g += 'LOCAL_PRELINK_MODULE := false\n'
			g += 'LOCAL_LDLIBS    := -llog -landroid -lEGL -lGLESv1_CM\n'
			g += 'LOCAL_STATIC_LIBRARIES := %s\n' %staticnames.strip()
			g += 'include $(BUILD_SHARED_LIBRARY) \n'
			g += '$(call import-module,android/native_app_glue)\n'

		mk.write( g )
		print('_'*80); print( g ); print('_'*80)
		return mk






class _rpy_func_wrapper(object):		# NOT RPYTHON
	def __init__(self,func):
		self.function = func		# this is replaced upon call to RPython.cache()
	def __call__( self, *args, **kw ):
		return self.function( *args, **kw )

class _rpy_func_bind(object):		# @sub-decorator
	# NOT RPYTHON, because of __call__
	# pypy reports error: File "../../pypy/pypy/annotation/bookkeeper.py", line 530, in getdesc pyobj,))
	#Exception: unexpected prebuilt constant: <rpythonic.rpythonic._rpy_func_bind object at 0xb766ab8c>
	def __init__(self,kw, stackless=False):
		self.argtypes = kw
		self.stackless = stackless

	def __call__(self,func):
		self.function = func
		self.name = func.func_name
		self.arguments = args = []

		spec = inspect.getargspec( func )
		if spec.args: nargs = len(spec.args)
		else: nargs = 0
		#if func.func_defaults and nargs != len(func.func_defaults): assert 0		# TODO
		#elif not func.func_defaults and spec.args:		# force defaults
		#	for arg in spec.args: args.append( self.argtypes[arg] )
		#	func.func_defaults = tuple( args )

		if spec.args:
			if self.argtypes:
				for arg in spec.args: args.append( self.argtypes[arg] )
			elif func.func_defaults:
				assert len(spec.args) == len(func.func_defaults)
				for i,arg in enumerate(spec.args): args.append( type(func.func_defaults[i]) )
			else:	# 'you must either provide keyword defaults or declare the types in the decorator'
				raise SyntaxError

		self.wrapper = _rpy_func_wrapper(func)
		return self.wrapper



class _rsingleton_(object):
	def __init__(self):
		self._locks_ = {}
	def _create_default_lock( self ): self._Lock_()

#class _RPY_(object): pass		# this works until you start dynamically adding things to _RPY_ from rpython-space
_RPY_ = _rsingleton_()		# just a container for the hacks below

def _rpy_object_getattr(self,name):
	#print 'get prop', name
	if hasattr( self._pointer.contents, 'mro_inst_'+name ):
		return getattr( self._pointer.contents, 'mro_inst_'+name )
	else:
		return getattr( self._pointer.contents, 's_inst_'+name )

def _rpy_threadsafe(func, lockname, *args ):	# RPYTHON
	if lockname in _RPY_._locks_:		# the lock is created Python-side, so it may or may not exist
		lock = _RPY_._locks_[ lockname ]
		lock.acquire()
		func(*args)
		lock.release()
	else:
		func(*args)

def _rpy_stackless_entrypoint( argc ):
	_RPY_._create_default_lock()
	_RPY_._stackless_()		# stackless entry point can take args anyways
	return 1


class _threadsafe(object):
	def __init__(self, name ):
		self.name = name		# semaphore name
	def __call__(self, func):
		setattr( _RPY_, '_threadsafe_%s' %func.func_name, func )
		e = 'lambda *args: _rpy_threadsafe(_RPY_._threadsafe_%s, "%s", *args)' %(func.func_name, self.name)
		g = eval( e )
		g.func_name = func.func_name
		return g

# the host may not allow this #
try:
	ctypes.CDLL('')
	ALLOWS_CTYPES_HOST_CDLL = True
except:
	ALLOWS_CTYPES_HOST_CDLL = False

if not ALLOWS_CTYPES_HOST_CDLL:
	class Lock(object): pass
else:
	class Lock(object):		# ctypes based thread lock with optional name
		lib = ctypes.CDLL('')
		_sem_open = lib.sem_open
		_sem_post = lib.sem_post
		_sem_wait = lib.sem_wait
		_sem_open.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.c_int, ctypes.c_uint]
		_sem_open.restype = ctypes.c_void_p
		_sem_post.argtypes = [ctypes.c_void_p]
		_sem_post.restype = ctypes.c_int
		_sem_wait.argtypes = [ctypes.c_void_p]
		_sem_wait.restype = ctypes.c_int

		def __init__(self, name=SEM_NAME, mode=600 ):
			self.name = name
			self.handle = self._sem_open( name, os.O_CREAT, mode, 1 )
			print( 'ctypes semaphore', self.handle )
		def acquire(self):
			a = self._sem_wait( self.handle )
			#print( 'acquire', a )
		def release(self):
			a = self._sem_post( self.handle )
			#print( 'release', a )

class AndroidEngine(object):	# RPYTHON
	def __init__(self, rpy):
		from pypy.rpython.lltypesystem import lltype, rffi
		self.rffi = rffi
		self.lltype = lltype
		self.rpy = rpy
		self.x = self.y = 0
		self.glue = rpy.rimport('android_native_app_glue')
		#ptr = lltype.Ptr( self.glue.android_app )
		#self.get_android_app_state = rffi.llexternal('_get_android_app_state_', [], ptr )
		self.EGL = rpy.rimport( 'EGL' )

		self.on_save_state = None
		self.on_terminate_window = None
		self.on_focus_gained = None
		self.on_focus_lost = None


	def setup(self):	# RPYTHON: user must call this before any other function
		self.app = self.get_android_app_state()
		self.app.onAppCmd( self.command_handler )	# takes *args
		self.app.onInputEvent( self.input_handler )

	def input_handler( self, app, event ):
		glue = self.glue
		t = glue.AInputEvent_getType(event)
		if t == glue.AINPUT_EVENT_TYPE_MOTION:
			self.x = glue.AMotionEvent_getX(event, 0)
			self.y = glue.AMotionEvent_getY(event, 0)
			return 1
		else: return 0


	def command_handler(self, app, cmd ):
		glue = self.glue
		if cmd == glue.APP_CMD_SAVE_STATE:
			if self.on_save_state: self.on_save_state( self )
		elif cmd == glue.APP_CMD_INIT_WINDOW:
			self.setup_display()
		elif cmd == glue.APP_CMD_TERM_WINDOW:
			if self.on_terminate_window: self.on_terminate_window( self )

		elif cmd == glue.APP_CMD_GAINED_FOCUS:
			if self.on_focus_gained: self.on_focus_gained( self )

		elif cmd == glue.APP_CMD_LOST_FOCUS:
			if self.on_focus_lost: self.on_focus_lost( self )



	def setup_display( self ):
		from pypy.rpython.lltypesystem import lltype, rffi
		#rffi = self.rffi
		e = self.EGL
		display = e.eglGetDisplay( e.EGL_DEFAULT_DISPLAY)
		self.display = display	# void-pointer
		e.eglInitialize(display, 0, 0)

		# Here specify the attributes of the desired configuration.
		# Below, we select an EGLConfig with at least 8 bits per color
		# component compatible with on-screen windows
		attribs = [
			e.EGL_SURFACE_TYPE,
			e.EGL_WINDOW_BIT,
			e.EGL_BLUE_SIZE, 8,
			e.EGL_GREEN_SIZE, 8,
			e.EGL_RED_SIZE, 8,
			e.EGL_NONE
		]
		config = self.lltype.voidp()

		# Here, the application chooses the configuration it desires. In this
		# sample, we have a very simplified selection process, where we pick
		# the first EGLConfig that matches our criteria.
		numConfigs = 1
		e.eglChooseConfig(display, attribs, config, 1, numConfigs)

		# EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
		# guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
		# As soon as we picked a EGLConfig, we can safely reconfigure the
		# ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. 
		e.eglGetConfigAttrib(display, config, e.EGL_NATIVE_VISUAL_ID, format)

		e.ANativeWindow_setBuffersGeometry(self.app.window, 0, 0, format)

		surface = e.eglCreateWindowSurface(display, config, self.app.window, rffi.NULL)
		context = e.eglCreateContext(display, config, rffi.NULL, rffi.NULL)

		e.eglMakeCurrent(display, surface, surface, context)

		#e.eglQuerySurface(display, surface, e.EGL_WIDTH, w)
		#e.eglQuerySurface(display, surface, e.EGL_HEIGHT, h)
		#self.width = w
		#self.height = h

		self.display = display
		self.context = context
		self.surface = surface


class LinuxPackage( object ):
	def __init__(self, exe ): self.path = exe.strpath
	def test(self): os.system( self.path )
	def get_executeable(self): return self.path
	def save(self, path ):
		os.system( 'cp -v %s %s' %(self.path, path) )

class RPython(object):
	@staticmethod
	def acquire_lock(): _RPY_._locks_[ SEM_NAME ].acquire()
	@staticmethod
	def release_lock(): _RPY_._locks_[ SEM_NAME ].release()

	def rimport( self, mname, namespace=None):
		#return _load( mname, type='rffi', platform=self.platform )
		mod = __import__( 
			'rffi%s.%s'%(self.platform, mname), 
			fromlist=[mname] 
		)
		if type(namespace) is dict:
			for n in dir(mod):
				if not n.startswith('_'): namespace[ n ] = getattr(mod,n)
		return mod


	def rcallback( self, signature_pointer, callback ):
		from pypy.rpython.annlowlevel import llhelper
		return llhelper(signature_pointer, callback)
	def get_rprint(self):	# required for javascript translation
		from pypy.rlib.debug import debug_print
		return debug_print



	def __init__(self, platform='linux', threading=False, gc='ref'):
		self.platform = platform
		if platform in 'android ios'.split():
			from pypy.rpython.lltypesystem import lltype, rffi
			self.rffi = rffi
			self.lltype = lltype
			if platform == 'android':
				self.engine = AndroidEngine( self )

		self._stackless = False
		self._threading = threading
		self._gc = gc
		self.wrapped = []
		self.classes = []
		#if stackless:
		if 1: self._setup()
		#except:
		#	print( 'pypy not found, this is ok if your only loading from cache' )
		#	self.AbstractThunk = object
		if threading: self._setup_threading()



	_STANDALONE_ = None
	def standalone( self, func ):		# decorator
		#func.func_name = 'rpythonic_main'
		RPython._STANDALONE_ = func
		if self.platform == 'android':
			exec( 'def entrypoint( app ): RPython._STANDALONE_( app ); return 1' )
		else: exec( 'def entrypoint( argv ): RPython._STANDALONE_(); return 1' )
		self._entrypoint_ = entrypoint
		return func

	def compile( self, inline=True, name='testing' ):
		mode = self.platform
		assert mode in 'linux javascript android'.split()
		if mode=='javascript':
			headers,sources = translate_rpython( self._entrypoint_, inline=False )
			_paths = [ c.strpath for c in sources ]
			_pypyinc = os.path.join(PATH2PYPY,'pypy/translator/c/')
			if _pypyinc not in INCLUDE_DIRS: INCLUDE_DIRS.append( _pypyinc )
			em = Emscripten(  paths = _paths, includes = [ _pypyinc ] )
			return em

		elif mode=='android':
			headers,sources = translate_rpython( self._entrypoint_, inline=inline )
			return AndroidPackage( headers, sources, name )

		elif mode=='linux':
			exe = translate_rpython( self._entrypoint_, inline=inline, gc='hybrid', compile=True )
			return LinuxPackage( exe )

	#def threadsafe(self, semname=SEM_NAME):
	#	if not self._threading: self._setup_threading()
	#	self._threading = True
	#	w = _threadsafe( semname )
	#	return w

	def _setup_threading(self):
		from pypy.rpython.lltypesystem import rffi, lltype
		self._threading = True
		@self.object
		class RLock(object):
			# The oflag argument specifies flags that control the operation of the call. If O_CREAT is specified in oflag, 
			# then the semaphore is created if it does not already exist. The owner (user ID) of the semaphore is set to
			# the effective user ID of the calling process. The group ownership (group ID) is set to the effective group
			# ID of the calling process. If both O_CREAT and O_EXCL are specified in oflag, then an error is returned
			# if a semaphore with the given name already exists.'''
			def __init__(self, name=SEM_NAME):
				print( 'creating new named semaphore: %s' %name )
				if name not in _RPY_._locks_: _RPY_._locks_[ name ] = self
				self.name = name
				charp = rffi.str2charp(name)
				#self.handle = _RPY_._sem_open( name, os.O_CREAT | os.O_EXCL, 0600, 0 )
				#self.handle = _RPY_._sem_open( name, os.O_CREAT, 0644, 1 )	# allow creation and recreation
				self.handle = _RPY_._sem_open( 
					charp, 
					rffi.cast(rffi.INT,os.O_CREAT), 
					rffi.cast(rffi.INT,600), 
					rffi.cast(rffi.UINT,1) 
				)	# allow creation and recreation
				print( 'sem handle', self.handle )
				#if self.handle == -1:
				#	print( 'SEM OPEN FAILED!' )
				#try: _RPY_._sem_unlink(self.name)
				#except OSError: print( 'SEMAPHORE UNLINK FAILED: %s' %self.name )

				#The sem_unlink subroutine removes the semaphore named by the string name.
				#If the semaphore named by name is currently referenced by other processes, then sem_unlink has no effect on the state of the semaphore. If one or more processes have the semaphore open when sem_unlink is called, destruction of the semaphore is postponed until all references to the semaphore have been destroyed by calls to sem_close, _exit, or exec. Calls to sem_open to recreate or reconnect to the semaphore refer to a new semaphore after sem_unlink is called.
				#The sem_unlink subroutine does not block until all references have been destroyed, and it returns immediately.

			#def unlink(self): _RPY_._locks_.remove( self.name )
			def release( self ): return _RPY_._sem_post( self.handle )
			def acquire( self ):
				block = True
				# sem_trywait() is the same as sem_wait(), except that if the decrement cannot be immediately performed, 
				# then call returns an error (errno set to EAGAIN) instead of blocking.
				if block: return _RPY_._sem_wait( self.handle )
				else: return _RPY_._sem_trywait( self.handle )

		self.RLock = RLock
		_RPY_._RLock_ = RLock



	def _setup(self):
		import pypy.rlib.rcoroutine
		d = pypy.rlib.rcoroutine.make_coroutine_classes(object)
		RPython._stackless_syncstate = d['syncstate']
		RPython.Coroutine = d['Coroutine']
		RPython.AbstractThunk = d['AbstractThunk']
		import pypy.module._multiprocessing.interp_semaphore as mp
		#for n in '_sem_open _sem_unlink _sem_wait _sem_trywait _sem_post _select'.split():
		#	setattr( _RPY_, n, getattr(mp,n) )

		from pypy.translator.tool.cbuild import ExternalCompilationInfo
		from pypy.rpython.tool import rffi_platform as platform
		from pypy.rpython.lltypesystem import rffi, lltype
		eci = ExternalCompilationInfo(
			includes = ['sys/time.h',
			'limits.h',
			'semaphore.h'],
			libraries = ['rt'],
		)
		class CConfig:
			_compilation_info_ = eci
			TIMEVAL = platform.Struct('struct timeval', [('tv_sec', rffi.LONG), ('tv_usec', rffi.LONG)])
			TIMESPEC = platform.Struct('struct timespec', [('tv_sec', rffi.TIME_T), ('tv_nsec', rffi.LONG)])
			SEM_FAILED = platform.ConstantInteger('SEM_FAILED')
			SEM_VALUE_MAX = platform.ConstantInteger('SEM_VALUE_MAX')
			SEM_TIMED_WAIT = platform.Has('sem_timedwait')
		config = platform.configure(CConfig)
		TIMEVAL        = config['TIMEVAL']
		TIMESPEC       = config['TIMESPEC']
		TIMEVALP       = rffi.CArrayPtr(TIMEVAL)
		TIMESPECP      = rffi.CArrayPtr(TIMESPEC)
		SEM_T          = rffi.COpaquePtr('sem_t', compilation_info=eci)
		## even without a wrapper and holding the GIL, it is still not threadsafe
		def external(name, args, result):
			return rffi.llexternal(name, args, result, compilation_info=eci, threadsafe=True, _nowrapper=True)
		_RPY_._sem_open = external('sem_open', [rffi.CCHARP, rffi.INT, rffi.INT, rffi.UINT], SEM_T)
		_RPY_._sem_unlink = external('sem_unlink', [rffi.CCHARP], rffi.INT)
		_RPY_._sem_wait = external('sem_wait', [SEM_T], rffi.INT)
		_RPY_._sem_trywait = external('sem_trywait', [SEM_T], rffi.INT)
		_RPY_._sem_post = external('sem_post', [SEM_T], rffi.INT)
		_RPY_._sem_getvalue = external('sem_getvalue', [SEM_T, rffi.INTP], rffi.INT)


	def get_function( self, name ):
		for fw in self.wrapped:
			if fw.name == name: return fw

	def get_class( self, name ):
		for w in self.classes:
			if w.__name__ == name: return w

	def object(self, klass):
		n = klass.__name__
		setattr( _RPY_, n, klass )
		rpyinit = eval('lambda : _RPY_.%s()'%n)
		rpyinit.func_name = '_rpyinit_%s' %n
		setattr( _RPY_, '_rpyinit_%s'%n, rpyinit )
		self.classes.append( klass )
		return klass	# must return same classobject because pypy needs to see it

	def stackless(self, func):
		assert not self._stackless		# only one stackless loop allowed
		self._stackless = True
		if not self._threading: self._setup_threading()
		self._threading = True
		fw = _rpy_func_bind( {}, stackless=True )
		fw( func )
		self.wrapped.append( fw )
		_RPY_._stackless_ = func
		return fw.wrapper

	def bind(self, **kw ):					# @decorator
		fw = _rpy_func_bind( kw )
		self.wrapped.append( fw )
		return fw


	def cache(self, mname, refresh=False):		# loads and compiles rpython module for CPython
		if refresh:
			self._gen( mname )
			mod = load( mname, debug=True )
		else:
			try: mod = load( mname, debug=True )
			except:
				self._gen( mname )
				mod = load( mname, debug=True )

		#if hasattr( mod, 'pypy_g_frameworkgc_setup' ):
		#	print('setting up pypy framework gc')
		#	mod.pypy_g_frameworkgc_setup()
		mod.instrument_setup()
		mod.RPython_StartupCode()
		#stackless#mod.pypy_g_slp_entry_point


		rpyinits = {}	# classname : ctypes function
		for n in mod.RPYTHONIC_WRAPPER_FUNCTIONS:
			if n.startswith('pypy_g_'):
				m = n[7:]
				fw = self.get_function( m )
				if m.startswith('_rpyinit_'):
					assert not fw
					klass = m.split('_rpyinit_')[-1]
					rpyinits[ klass ] = mod.RPYTHONIC_WRAPPER_FUNCTIONS[ n ]

				elif fw:
					print( 'replacing python function with ctypes function', fw.name )
					if fw.stackless:
						fw.wrapper.function = mod.RPYTHONIC_WRAPPER_FUNCTIONS[ 'pypy_g_slp_entry_point' ]
					else: fw.wrapper.function = mod.RPYTHONIC_WRAPPER_FUNCTIONS[ n ]

		for klass in self.classes:
			n = klass.__name__
			mpath = klass.__module__.replace('.','_')
			print( 'Shared-Object: %s' %n )
			struct = getattr( mod, 'pypy_%s_%s0' %(mpath,n) )
			assert n in rpyinits

			setattr( klass, '__getattr__', _rpy_object_getattr )
			#setattr( klass, '_rpystruct_', struct )
			#klass.__dict__ = struct.__dict__.copy()	# why readonly?
			klass._rpythonic_ = True	# tells wrapper methods to use self._pointer
			# TODO get struct safe API, struct()(type=True) fails
			klass._rpytype_ = ctypes.POINTER(struct._ctypes_struct_)
			klass._rpyinit_ = rpyinits[n]


			la = lambda s, *args: setattr( s, '_pointer', ctypes.cast(s._rpyinit_(*args),s._rpytype_)  )
			setattr( klass, '__init__', la )		# changes init of class to call generated rpyinit, that returns ctypes pointer

			for f in mod.RPYTHONIC_WRAPPER_FUNCTIONS:
				prefix = 'pypy_g_%s_'%n
				if f.startswith(prefix):
					methname = f[ len(prefix) : ]
					#print '	meth name:', methname
					if methname in dir( klass ):
						#print '	class contains method:', methname
						if not methname.startswith('_'):
							print( 'replaced method with compiled method: %s' %methname )
							setattr( klass, '_rpymethod_%s' %methname, mod.RPYTHONIC_WRAPPER_FUNCTIONS[ f ] )
							lamb = eval( 'lambda _self, *args, **kw: _self._rpymethod_%s(_self,*args,**kw)' %methname )
							setattr( klass, methname, lamb )
					else:
						print( 'WARN: meth not found in class', n )



		return mod


	def _gen(self,mname):
		global CTYPES_OUTPUT

		#g = ['_float=.0; _int=0']	# this wont work because pypy is too smart
		g = [									# workaround, declare non-concrete variables
			'from pypy.rlib import streamio',
			's = streamio.fdopen_as_stream(1, "r", 0)',
			'_int = int( s.read(1) )',
			'_float = float( s.read(1) )',
			'_str = str( s.read(2) )',
			'_char = str( s.read(1) )',
			'_bool = bool( _int )',
			'_float_list = [ _float for i in range(_int) ]',
		]

		for klass in self.classes:
			n = klass.__name__

			#if self._threading and klass is self.Lock:
			#	g.append( '_RPY_._lock_ = _RPY_._new_%s()' %n )
			#	g.append( 'a = _RPY_._lock_' )
			#else:
			g.append( 'a = _RPY_._rpyinit_%s()' %n )	# could do proper init here, and below class() no-args
			g.append( 'print str(a)' )


			for aname in dir( klass ):
				attr = getattr( klass, aname )
				if not aname.startswith('_') and inspect.ismethod(attr):
					spec = inspect.getargspec( attr )
					s = ''
					if spec.args:
						for ai,an in enumerate(spec.args):
							if ai == 0: continue
							arg = type( spec.defaults[ ai-1 ] )
							if arg is int: s += '_int,'
							elif arg is float: s += '_float,'
							elif arg is str: s += '_str,'
							elif arg is ctypes.c_char: s += '_char,'
							elif arg is bool: s += '_bool,'
							else: s += '%s(),' %arg.__name__		# TODO, this is tricky

					g.append( 'b = a.%s( %s )'%(aname,s) )
					g.append( 'print str(b)' )

		for i,fw in enumerate(self.wrapped):
			if fw.name == '__init__': continue
			setattr(_RPY_, fw.name, fw.function)
			s = 'val%s = _RPY_.%s(' %(i,fw.name)		# tricky - part1
			for arg in fw.arguments:	# TODO detect global singleton instances?
				if arg is int: s += '_int,'
				elif arg is float: s += '_float,'
				elif arg is str: s += '_str,'
				elif arg is ctypes.c_char: s += '_char,'
				elif arg is bool: s += '_bool,'
				elif type(arg) is list:
					#s += '[_float],'
					s += '_float_list,'
				else: s += '%s(),' %arg.__name__		# TODO, tricky - save instances above?
			s += ')'
			g.append( s )
			g.append( 'print val%s' %i )		# tricky - part2: prevents pypy from inlining

		g.insert(0, 'print "generated ugly declare - this should never be called directly"' )
		#g.append( 'return 1' )
		g = 'def declare_ugly():\n\t' + '\n\t'.join( g )
		print( g )
		exec( g )
		print( '--------compiling rpython-----------' )
		# TODO how to tell inliner not to inline in main?  pypy.translator.backendopt.inline.inlining_heuristic
		# inliner just moves OP_INT_ADD into main...
		secondary_entrypoints = []
		secondary_entrypoints.append( (declare_ugly, []) )

		if self._stackless: entry = _rpy_stackless_entrypoint
		else: entry = lambda x: 1

		headers,sources = translate_rpython( 
			entry, 
			inline=False, 
			stackless=self._stackless, 
			gc=self._gc,
			functions=secondary_entrypoints,
		)
		pypygen = {}
		_paths = []
		for c in sources:
			pypygen[ c.basename ] = c
			_paths.append( c.strpath )
			print( c.strpath )

		_pypyinc = os.path.join(PATH2PYPY,'pypy/translator/c/')
		if _pypyinc not in INCLUDE_DIRS: INCLUDE_DIRS.append( _pypyinc )		# contains src/

		testlib = GCC.compile( 
			mname,
			paths = _paths,
			includes = [ _pypyinc ],
			gc=self._gc,
		)

		pre = os.path.join( 'genctypes', mname )
		mdir = os.path.join( CACHEDIR, pre )
		if not os.path.isdir( mdir ):
			os.makedirs( mdir )
			open( os.path.join(os.path.join(CACHEDIR,'genctypes'),'__init__.py'), 'wb' )
		CTYPES_OUTPUT = os.path.join(pre,'__init__.py')
		LIBS.append( 'lib%s.so' %mname)
		a = C( pypygen['implement.c'].strpath, debug=0 )
		a.save()

		print( g )



